barathvasan-dev commited on
Commit
279c530
Β·
1 Parent(s): 25c874b

Refactor: Consolidate AI Investigation into single unified agent tab with RAG capabilities

Browse files

- Replace 4 separate investigation tabs (Vehicle, Area, Midnight, Most Suspicious) with 1 unified tab
- Add ask_investigation_question() function for natural language queries with RAG
- Agent now dynamically understands questions and fetches relevant database data
- Users can ask any question (e.g. 'vehicles in Adyar at midnight', 'suspicious activity in industrial zones')
- Mistral model acts as RAG agent to analyze data and provide intelligent answers
- Add example quick-start buttons for common investigation types

Files changed (2) hide show
  1. ai_investigation.py +281 -3
  2. app.py +122 -159
ai_investigation.py CHANGED
@@ -536,6 +536,283 @@ def find_most_suspicious_today():
536
  # FORMAT OUTPUT
537
  # =====================================================
538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  def format_investigation_output(result):
540
  """Format investigation result for display"""
541
  if result.get("status") == "error":
@@ -544,11 +821,12 @@ def format_investigation_output(result):
544
  }
545
 
546
  return {
547
- "query": result.get("plate") or result.get("location") or "Midnight Activity",
548
  "investigation_narrative": result.get("investigation_narrative", ""),
 
549
  "analysis": result.get("analysis", {}),
550
- "key_findings": _extract_key_findings(result),
551
- "recommendations": _extract_recommendations(result.get("investigation_narrative", ""))
552
  }
553
 
554
 
 
536
  # FORMAT OUTPUT
537
  # =====================================================
538
 
539
+ # =====================================================
540
+ # UNIFIED AI AGENT - NATURAL LANGUAGE QUESTIONS
541
+ # =====================================================
542
+
543
+ def ask_investigation_question(question):
544
+ """
545
+ Unified AI Investigation Agent
546
+
547
+ Takes any natural language question and:
548
+ 1. Uses Mistral to understand what data is needed
549
+ 2. Queries the database based on the question intent
550
+ 3. Uses Mistral to generate intelligent answer with RAG
551
+ """
552
+
553
+ if not question or len(question.strip()) < 3:
554
+ return {
555
+ "status": "error",
556
+ "message": "Please enter a valid question"
557
+ }
558
+
559
+ client = get_mistral_client()
560
+
561
+ # Step 1: Understand query intent
562
+ intent_prompt = f"""Analyze this surveillance question and determine what type of data is needed.
563
+
564
+ Question: {question}
565
+
566
+ Respond with ONLY one of these intents in the format:
567
+ INTENT: [type]
568
+ QUERY: [specific value or description]
569
+
570
+ Types and examples:
571
+ 1. VEHICLE - plate number like "TN12UP3854" or "find vehicle in [location]"
572
+ 2. LOCATION - area name like "Adyar", "Nungambakkam", "Guindy"
573
+ 3. MIDNIGHT - late night activity in specific area or general
574
+ 4. ACTIVITY - activity patterns in area during specific times
575
+ 5. SUSPICIOUS - find suspicious vehicles matching criteria
576
+ 6. TIME_RANGE - activity in specific time period or area
577
+
578
+ Extract the specific target from the question (plate, location, time, etc)."""
579
+
580
+ try:
581
+ if client:
582
+ intent_response = client.chat(
583
+ model="mistral-small",
584
+ messages=[{"role": "user", "content": intent_prompt}],
585
+ temperature=0.3,
586
+ max_tokens=100
587
+ )
588
+ intent_text = intent_response.choices[0].message.content
589
+ else:
590
+ intent_text = _parse_intent_fallback(question)
591
+
592
+ print(f"Intent parsed: {intent_text}")
593
+
594
+ # Step 2: Execute query based on intent
595
+ data_context = _execute_intelligent_query(intent_text, question)
596
+
597
+ # Step 3: Generate answer with RAG
598
+ answer = _generate_rag_answer(question, data_context, client)
599
+
600
+ return {
601
+ "status": "success",
602
+ "question": question,
603
+ "answer": answer,
604
+ "data_summary": data_context.get("summary", ""),
605
+ "analysis": data_context.get("analysis", {}),
606
+ "findings": data_context.get("findings", [])
607
+ }
608
+
609
+ except Exception as e:
610
+ print(f"Error in investigation: {e}")
611
+ return {
612
+ "status": "error",
613
+ "message": f"Investigation failed: {str(e)}"
614
+ }
615
+
616
+
617
+ def _execute_intelligent_query(intent_text, question):
618
+ """Execute database query based on parsed intent"""
619
+
620
+ intent_text = intent_text.upper()
621
+ data = {
622
+ "summary": "",
623
+ "analysis": {},
624
+ "findings": [],
625
+ "raw_data": None
626
+ }
627
+
628
+ try:
629
+ # Parse intent and query
630
+ if "INTENT: VEHICLE" in intent_text:
631
+ # Extract plate from question or intent
632
+ plate = _extract_plate_from_text(intent_text + " " + question)
633
+ if plate:
634
+ df = get_vehicle_detections(plate, days=30)
635
+ if not df.empty:
636
+ analysis = analyze_vehicle_behavior(df)
637
+ data["analysis"] = analysis
638
+ data["summary"] = f"Found {len(df)} detections for vehicle {plate}"
639
+ data["findings"] = _extract_key_findings({"analysis": analysis})
640
+ data["raw_data"] = df.to_dict(orient="records")[:10]
641
+
642
+ elif "INTENT: LOCATION" in intent_text:
643
+ # Extract location
644
+ location = _extract_location_from_text(intent_text + " " + question)
645
+ if location:
646
+ df = get_area_activity(location, days=7)
647
+ if not df.empty:
648
+ analysis = analyze_area_patterns(df)
649
+ data["analysis"] = analysis
650
+ data["summary"] = f"Found {len(df)} detections in {location}"
651
+ data["findings"] = _extract_key_findings({"analysis": analysis})
652
+ data["raw_data"] = df.to_dict(orient="records")[:10]
653
+
654
+ elif "INTENT: MIDNIGHT" in intent_text:
655
+ # Extract location if mentioned
656
+ location = _extract_location_from_text(question)
657
+ df = get_midnight_activity(hours_start=22, hours_end=4, days=7)
658
+
659
+ if location:
660
+ df = df[df["location"].str.contains(location, case=False, na=False)]
661
+
662
+ if not df.empty:
663
+ analysis = analyze_midnight_patterns(df)
664
+ data["analysis"] = analysis
665
+ data["summary"] = f"Found {len(df)} midnight detections"
666
+ data["findings"] = _extract_key_findings({"analysis": analysis})
667
+ data["raw_data"] = df.to_dict(orient="records")[:10]
668
+
669
+ elif "INTENT: ACTIVITY" in intent_text:
670
+ # Get activity in specific area
671
+ location = _extract_location_from_text(question)
672
+ if location:
673
+ df = get_area_activity(location, days=7)
674
+ if not df.empty:
675
+ analysis = analyze_area_patterns(df)
676
+ data["analysis"] = analysis
677
+ data["summary"] = f"Analyzed activity in {location}"
678
+ data["findings"] = _extract_key_findings({"analysis": analysis})
679
+ data["raw_data"] = df.to_dict(orient="records")[:10]
680
+
681
+ if not data["summary"]:
682
+ # Fallback: get general statistics
683
+ query = "SELECT COUNT(*) as total_detections, COUNT(DISTINCT plate) as unique_vehicles, COUNT(DISTINCT location) as unique_locations FROM vehicle_logs WHERE date >= CURRENT_DATE - INTERVAL '7 days'"
684
+ with engine.connect() as conn:
685
+ result = conn.execute(text(query))
686
+ row = result.fetchone()
687
+ if row:
688
+ data["summary"] = f"Database has {row[0]} total detections, {row[1]} unique vehicles, {row[2]} unique locations"
689
+
690
+ except Exception as e:
691
+ print(f"Error executing query: {e}")
692
+ data["summary"] = "Unable to fetch data from database"
693
+
694
+ return data
695
+
696
+
697
+ def _generate_rag_answer(question, data_context, client):
698
+ """Generate RAG-based answer using Mistral"""
699
+
700
+ context_str = json.dumps(data_context, indent=2, default=str)
701
+
702
+ rag_prompt = f"""You are a professional surveillance and traffic analysis AI agent.
703
+
704
+ User Question: {question}
705
+
706
+ Available Data:
707
+ {context_str}
708
+
709
+ Generate a detailed, professional answer to the user's question using the available data.
710
+
711
+ Guidelines:
712
+ 1. Answer directly and concisely
713
+ 2. Use the data to provide specific insights
714
+ 3. Highlight patterns, anomalies, and recommendations
715
+ 4. Be factual and data-driven
716
+ 5. If data is limited, explain what additional data would help
717
+
718
+ Format your response in a clear, organized manner with sections if needed."""
719
+
720
+ try:
721
+ if client:
722
+ response = client.chat(
723
+ model="mistral-small",
724
+ messages=[{"role": "user", "content": rag_prompt}],
725
+ temperature=0.3,
726
+ max_tokens=800
727
+ )
728
+ return response.choices[0].message.content
729
+ else:
730
+ return _generate_fallback_rag_answer(question, data_context)
731
+
732
+ except Exception as e:
733
+ print(f"Error generating answer: {e}")
734
+ return _generate_fallback_rag_answer(question, data_context)
735
+
736
+
737
+ def _generate_fallback_rag_answer(question, data_context):
738
+ """Fallback answer generation without Mistral"""
739
+
740
+ answer = f"**Analysis for:** {question}\n\n"
741
+ answer += f"**Summary:** {data_context.get('summary', 'No data available')}\n\n"
742
+
743
+ if data_context.get("analysis"):
744
+ answer += "**Key Metrics:**\n"
745
+ for key, value in list(data_context["analysis"].items())[:5]:
746
+ answer += f"β€’ {key}: {value}\n"
747
+
748
+ if data_context.get("findings"):
749
+ answer += "\n**Findings:**\n"
750
+ for finding in data_context["findings"]:
751
+ answer += f"β€’ {finding}\n"
752
+
753
+ return answer
754
+
755
+
756
+ def _extract_plate_from_text(text):
757
+ """Extract license plate from text"""
758
+ import re
759
+ # Look for plate patterns like "TN12UP3854" or "TN 12 UP 3854"
760
+ plate_patterns = [
761
+ r'([A-Z]{2}\d{2}[A-Z]{2}\d{4})',
762
+ r'([A-Z]{2} \d{2} [A-Z]{2} \d{4})',
763
+ ]
764
+
765
+ for pattern in plate_patterns:
766
+ match = re.search(pattern, text.upper())
767
+ if match:
768
+ return match.group(1).replace(" ", "")
769
+
770
+ return None
771
+
772
+
773
+ def _extract_location_from_text(text):
774
+ """Extract location from text"""
775
+ # Common locations in Chennai
776
+ locations = [
777
+ "adyar", "anna nagar", "nungambakkam", "guindy", "chetpet",
778
+ "tnagar", "t nagar", "mylapore", "velachery", "tambaram",
779
+ "chromepet", "madhavaram", "tiruvottiyur", "ayanavaram",
780
+ "anna salai", "mg road", "mount road", "geek city",
781
+ "industrial estate", "delphi", "siruseri", "sholinganallur"
782
+ ]
783
+
784
+ text_lower = text.lower()
785
+ for location in locations:
786
+ if location in text_lower:
787
+ return location.title()
788
+
789
+ # Try to find any capitalized word that might be a location
790
+ import re
791
+ words = re.findall(r'\b[A-Z][a-z]+(?:\s[A-Z][a-z]+)*\b', text)
792
+ if words:
793
+ return words[-1]
794
+
795
+ return None
796
+
797
+
798
+ def _parse_intent_fallback(question):
799
+ """Fallback intent parsing without Mistral"""
800
+ q_lower = question.lower()
801
+
802
+ if any(word in q_lower for word in ["vehicle", "plate", "car", "auto"]):
803
+ return "INTENT: VEHICLE"
804
+ elif any(word in q_lower for word in ["midnight", "night", "late", "early morning", "2am", "3am"]):
805
+ return "INTENT: MIDNIGHT"
806
+ elif any(word in q_lower for word in ["area", "location", "place", "zone", "street", "road"]):
807
+ return "INTENT: LOCATION"
808
+ elif any(word in q_lower for word in ["activity", "movement", "traffic", "busy"]):
809
+ return "INTENT: ACTIVITY"
810
+ elif any(word in q_lower for word in ["suspicious", "alert", "danger", "risk", "threat"]):
811
+ return "INTENT: SUSPICIOUS"
812
+ else:
813
+ return "INTENT: ACTIVITY"
814
+
815
+
816
  def format_investigation_output(result):
817
  """Format investigation result for display"""
818
  if result.get("status") == "error":
 
821
  }
822
 
823
  return {
824
+ "query": result.get("plate") or result.get("location") or "Investigation",
825
  "investigation_narrative": result.get("investigation_narrative", ""),
826
+ "answer": result.get("answer", ""),
827
  "analysis": result.get("analysis", {}),
828
+ "key_findings": result.get("findings", []),
829
+ "recommendations": _extract_recommendations(result.get("investigation_narrative", "") or result.get("answer", ""))
830
  }
831
 
832
 
app.py CHANGED
@@ -40,6 +40,7 @@ from ai_investigation import (
40
  investigate_area,
41
  investigate_midnight_activity,
42
  find_most_suspicious_today,
 
43
  format_investigation_output
44
  )
45
 
@@ -99,6 +100,56 @@ def investigate_area_wrapper(location):
99
  )
100
 
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  def investigate_midnight_wrapper():
103
  """Wrapper for midnight activity investigation"""
104
  result = investigate_midnight_activity()
@@ -623,181 +674,93 @@ with gr.Blocks(
623
 
624
 
625
  # =====================================================
626
- # TAB 3: AI Assistant
627
  # =====================================================
628
 
629
  with gr.Tab("πŸ” AI Investigation", id="tab_investigation"):
630
 
631
  gr.Markdown("""
632
- # πŸ” AI Investigation Assistant
 
 
 
 
 
 
633
 
634
- Professional surveillance analysis system. Analyzes vehicle behavior patterns and generates intelligence narratives.
 
 
 
 
 
 
 
635
  """)
636
-
637
- with gr.Tabs():
638
-
639
- # =====================================================
640
- # INVESTIGATION TYPE 1: VEHICLE
641
- # =====================================================
642
-
643
- with gr.Tab("πŸš— Vehicle Investigation"):
644
- gr.Markdown("### Investigate single vehicle behavior")
645
-
646
- invest_plate = gr.Textbox(
647
- placeholder="TN12UP3854",
648
- label="License Plate",
649
- scale=2
650
- )
651
-
652
- investigate_btn = gr.Button("πŸ”Ž Investigate Vehicle", variant="primary")
653
-
654
- with gr.Row():
655
- invest_narrative = gr.Textbox(
656
- label="πŸ“‹ Investigation Narrative",
657
- lines=8,
658
- interactive=False
659
- )
660
-
661
- invest_findings = gr.Textbox(
662
- label="🚨 Key Findings",
663
- lines=8,
664
- interactive=False
665
- )
666
-
667
- invest_analysis = gr.JSON(
668
- label="πŸ“Š Detailed Analysis"
669
- )
670
-
671
- invest_recommendations = gr.Textbox(
672
- label="βœ… Recommendations",
673
- lines=6,
674
- interactive=False
675
- )
676
-
677
- investigate_btn.click(
678
- fn=investigate_vehicle_wrapper,
679
- inputs=[invest_plate],
680
- outputs=[invest_narrative, invest_findings, invest_analysis, invest_recommendations]
681
- )
682
-
683
- # =====================================================
684
- # INVESTIGATION TYPE 2: AREA
685
- # =====================================================
686
 
687
- with gr.Tab("πŸ“ Area Intelligence"):
688
- gr.Markdown("### Analyze activity in specific area/location")
689
-
690
- invest_location = gr.Textbox(
691
- placeholder="Nungambakkam_HighRoad or Guindy",
692
- label="Location/Area Name",
693
- scale=2
694
- )
695
-
696
- area_investigate_btn = gr.Button("πŸ”Ž Analyze Area", variant="primary")
697
-
698
- with gr.Row():
699
- area_narrative = gr.Textbox(
700
- label="πŸ“‹ Area Intelligence Report",
701
- lines=8,
702
- interactive=False
703
- )
704
-
705
- area_findings = gr.Textbox(
706
- label="🚨 Key Findings",
707
- lines=8,
708
- interactive=False
709
- )
710
-
711
- area_analysis = gr.JSON(
712
- label="πŸ“Š Area Analysis"
713
- )
714
-
715
- area_recommendations = gr.Textbox(
716
- label="βœ… Security Recommendations",
717
- lines=6,
718
  interactive=False
719
  )
720
-
721
- area_investigate_btn.click(
722
- fn=investigate_area_wrapper,
723
- inputs=[invest_location],
724
- outputs=[area_narrative, area_findings, area_analysis, area_recommendations]
725
- )
726
 
727
- # =====================================================
728
- # INVESTIGATION TYPE 3: MIDNIGHT ACTIVITY
729
- # =====================================================
730
-
731
- with gr.Tab("πŸŒ™ Midnight Activity"):
732
- gr.Markdown("### Find suspicious midnight/late-night movements")
733
-
734
- midnight_btn = gr.Button("πŸ”Ž Analyze Midnight Activity", variant="primary", scale=2)
735
-
736
- with gr.Row():
737
- midnight_narrative = gr.Textbox(
738
- label="πŸ“‹ Night Activity Report",
739
- lines=8,
740
- interactive=False
741
- )
742
-
743
- midnight_findings = gr.Textbox(
744
- label="🚨 Suspicious Patterns",
745
- lines=8,
746
- interactive=False
747
- )
748
-
749
- midnight_analysis = gr.JSON(
750
- label="πŸ“Š Night Analysis"
751
- )
752
-
753
- midnight_recommendations = gr.Textbox(
754
- label="βœ… Tactical Recommendations",
755
- lines=6,
756
  interactive=False
757
  )
758
-
759
- midnight_btn.click(
760
- fn=investigate_midnight_wrapper,
761
- inputs=[],
762
- outputs=[midnight_narrative, midnight_findings, midnight_analysis, midnight_recommendations]
763
- )
764
-
765
- # =====================================================
766
- # INVESTIGATION TYPE 4: TODAY'S MOST SUSPICIOUS
767
- # =====================================================
768
 
769
- with gr.Tab("⚠️ Most Suspicious Today"):
770
- gr.Markdown("### Automatically find today's most suspicious vehicle")
771
-
772
- suspicious_btn = gr.Button("πŸ”Ž Find Most Suspicious", variant="primary", scale=2)
773
-
774
- with gr.Row():
775
- suspicious_narrative = gr.Textbox(
776
- label="πŸ“‹ Investigation Summary",
777
- lines=8,
778
- interactive=False
779
- )
780
-
781
- suspicious_findings = gr.Textbox(
782
- label="🚨 Alert Details",
783
- lines=8,
784
- interactive=False
785
- )
786
-
787
- suspicious_analysis = gr.JSON(
788
- label="πŸ“Š Detection Analysis"
789
  )
790
-
791
- suspicious_recommendations = gr.Textbox(
792
- label="βœ… Action Items",
793
- lines=6,
794
- interactive=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
795
  )
796
-
797
- suspicious_btn.click(
798
- fn=find_suspicious_wrapper,
799
- inputs=[],
800
- outputs=[suspicious_narrative, suspicious_findings, suspicious_analysis, suspicious_recommendations]
 
801
  )
802
 
803
  # =====================================================
 
40
  investigate_area,
41
  investigate_midnight_activity,
42
  find_most_suspicious_today,
43
+ ask_investigation_question,
44
  format_investigation_output
45
  )
46
 
 
100
  )
101
 
102
 
103
+ def investigate_midnight_wrapper():
104
+ """Wrapper for midnight investigation"""
105
+ result = investigate_midnight_activity()
106
+ if result.get("status") == "error":
107
+ error_msg = result.get("message", "Investigation failed")
108
+ return (f"❌ Error: {error_msg}", "", {}, "")
109
+
110
+ output = format_investigation_output(result)
111
+ return (
112
+ output.get("investigation_narrative", ""),
113
+ "\n".join(output.get("key_findings", [])),
114
+ output.get("analysis", {}),
115
+ "\n".join(output.get("recommendations", []))
116
+ )
117
+
118
+
119
+ def find_suspicious_wrapper():
120
+ """Wrapper for finding most suspicious today"""
121
+ result = find_most_suspicious_today()
122
+ if result.get("status") == "error":
123
+ error_msg = result.get("message", "Investigation failed")
124
+ return (f"❌ Error: {error_msg}", "", {}, "")
125
+
126
+ output = format_investigation_output(result)
127
+ return (
128
+ output.get("investigation_narrative", ""),
129
+ "\n".join(output.get("key_findings", [])),
130
+ output.get("analysis", {}),
131
+ "\n".join(output.get("recommendations", []))
132
+ )
133
+
134
+
135
+ def ask_question_wrapper(question):
136
+ """Unified AI Investigation Agent - handles any natural language question"""
137
+ if not question or len(question.strip()) < 3:
138
+ return ("Please ask a valid question about vehicles, locations, or activities", "", {}, "")
139
+
140
+ result = ask_investigation_question(question)
141
+ if result.get("status") == "error":
142
+ error_msg = result.get("message", "Investigation failed")
143
+ return (f"❌ Error: {error_msg}", "", {}, "")
144
+
145
+ return (
146
+ result.get("answer", ""),
147
+ "\n".join(result.get("findings", [])),
148
+ result.get("analysis", {}),
149
+ ""
150
+ )
151
+
152
+
153
  def investigate_midnight_wrapper():
154
  """Wrapper for midnight activity investigation"""
155
  result = investigate_midnight_activity()
 
674
 
675
 
676
  # =====================================================
677
+ # TAB 3: AI Assistant - Unified Investigation Agent
678
  # =====================================================
679
 
680
  with gr.Tab("πŸ” AI Investigation", id="tab_investigation"):
681
 
682
  gr.Markdown("""
683
+ # πŸ” AI Investigation Assistant - Smart Agent with RAG
684
+
685
+ Ask any question about vehicles, locations, or activities. The AI agent will:
686
+ - πŸ€– Understand your question
687
+ - πŸ“Š Fetch relevant data from the database
688
+ - 🧠 Analyze patterns and provide intelligent insights
689
+ - πŸ“‹ Generate detailed investigation reports
690
 
691
+ **Examples of questions you can ask:**
692
+ - "What vehicles were in Adyar at midnight?"
693
+ - "Show me suspicious activity in Nungambakkam between 10PM and 3AM"
694
+ - "Which vehicles are most active in industrial zones?"
695
+ - "What's the activity level in Guindy today?"
696
+ - "Find vehicles with multiple detections in different areas"
697
+ - "Which plates appear most frequently at night?"
698
+ - "Analyze traffic patterns in Anna Nagar"
699
  """)
700
+
701
+ with gr.Row():
702
+ question_input = gr.Textbox(
703
+ placeholder="e.g., What vehicles were detected in Adyar at midnight? Or: Show me suspicious activity in industrial zones tonight",
704
+ label="πŸ€” Ask Any Question About Vehicles, Locations, or Activities",
705
+ lines=3,
706
+ scale=5
707
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
 
709
+ investigate_btn = gr.Button("πŸ”Ž Investigate", variant="primary", scale=1, size="lg")
710
+
711
+ # Output sections
712
+ with gr.Tabs():
713
+ with gr.Tab("πŸ“‹ AI Response"):
714
+ response_output = gr.Textbox(
715
+ label="πŸ€– AI Investigation Analysis",
716
+ lines=12,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
  interactive=False
718
  )
 
 
 
 
 
 
719
 
720
+ with gr.Tab("🚨 Key Findings"):
721
+ findings_output = gr.Textbox(
722
+ label="Key Findings & Anomalies",
723
+ lines=10,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
724
  interactive=False
725
  )
 
 
 
 
 
 
 
 
 
 
726
 
727
+ with gr.Tab("πŸ“Š Data Analysis"):
728
+ analysis_output = gr.JSON(
729
+ label="Detailed Metrics & Statistics"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
730
  )
731
+
732
+ investigate_btn.click(
733
+ fn=ask_question_wrapper,
734
+ inputs=[question_input],
735
+ outputs=[response_output, findings_output, analysis_output, gr.State(value="")]
736
+ )
737
+
738
+ # Example questions
739
+ gr.Markdown("""
740
+ ### πŸ’‘ Quick Investigation Starters
741
+ Click any example to start exploring:
742
+ """)
743
+
744
+ example_questions = [
745
+ "What vehicles were in Adyar at midnight last night?",
746
+ "Show me suspicious midnight activity in industrial zones",
747
+ "Which plates appear most frequently in different locations?",
748
+ "Analyze activity patterns in Nungambakkam",
749
+ "Find vehicles with multiple detections in one day",
750
+ ]
751
+
752
+ with gr.Row():
753
+ for example in example_questions[:3]:
754
+ gr.Button(example, size="sm", variant="secondary").click(
755
+ fn=lambda q=example: ask_question_wrapper(q),
756
+ outputs=[response_output, findings_output, analysis_output]
757
  )
758
+
759
+ with gr.Row():
760
+ for example in example_questions[3:]:
761
+ gr.Button(example, size="sm", variant="secondary").click(
762
+ fn=lambda q=example: ask_question_wrapper(q),
763
+ outputs=[response_output, findings_output, analysis_output]
764
  )
765
 
766
  # =====================================================