Shakir60 commited on
Commit
c73686d
Β·
verified Β·
1 Parent(s): 838ba38

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +571 -65
app.py CHANGED
@@ -1,67 +1,573 @@
1
- from flask import Flask, request, jsonify
 
 
 
 
 
2
  import torch
3
- from transformers import AutoProcessor, AutoModelForImageClassification
 
 
4
  from sentence_transformers import SentenceTransformer
5
- import sqlite3
6
-
7
- app = Flask(__name__)
8
-
9
- # Load the defect detection model (open-source, hugging face model)
10
- DETECTION_MODEL_NAME = "microsoft/beit-base-patch16-224-pt22k-ft22k"
11
- processor = AutoProcessor.from_pretrained(DETECTION_MODEL_NAME)
12
- detection_model = AutoModelForImageClassification.from_pretrained(DETECTION_MODEL_NAME)
13
-
14
- defects_to_remedies = {
15
- "crack": "Fill cracks with epoxy. Structural cracks might need professional inspection.",
16
- "spalling": "Clean affected area and apply anti-corrosion primer before repairing.",
17
- "leakage": "Fix water source, seal with water-proofing compounds.",
18
- "mold": "Clean the mold, improve ventilation, and apply mold-resistant paint."
19
- }
20
-
21
- # Initialize a Sentence Transformer for text embeddings
22
- EMBEDDING_MODEL_NAME = "sentence-transformers/all-MiniLM-L6-v2"
23
- embedding_model = SentenceTransformer(EMBEDDING_MODEL_NAME)
24
-
25
- # SQLite database setup
26
- db_conn = sqlite3.connect('defects.db', check_same_thread=False)
27
- c = db_conn.cursor()
28
- c.execute('''CREATE TABLE IF NOT EXISTS defects (id INTEGER PRIMARY KEY, defect TEXT, remedy TEXT, embedding BLOB)''')
29
- db_conn.commit()
30
-
31
- # Populate defect remedies table if empty
32
- def seed_database():
33
- for defect, remedy in defects_to_remedies.items():
34
- c.execute("SELECT * FROM defects WHERE defect=?", (defect,))
35
- if not c.fetchone():
36
- embedding = embedding_model.encode(remedy).tolist()
37
- c.execute("INSERT INTO defects (defect, remedy, embedding) VALUES (?, ?, ?)", (defect, remedy, str(embedding)))
38
- db_conn.commit()
39
-
40
- seed_database()
41
-
42
- @app.route('/detect', methods=['POST'])
43
- def detect_defect():
44
- if 'image' not in request.files:
45
- return jsonify({"error": "No image uploaded."}), 400
46
-
47
- image = request.files['image'].read()
48
-
49
- # Preprocess and predict the defect
50
- inputs = processor(images=image, return_tensors="pt")
51
- outputs = detection_model(**inputs)
52
- probs = torch.nn.functional.softmax(outputs.logits, dim=1)
53
- predicted_class = torch.argmax(probs, dim=1)
54
- class_name = detection_model.config.id2label[predicted_class.item()]
55
-
56
- # Query remedy
57
- c.execute("SELECT remedy FROM defects WHERE defect=?", (class_name,))
58
- row = c.fetchone()
59
- if row:
60
- remedy = row[0]
61
- else:
62
- remedy = "No specific remedy available for this defect."
63
-
64
- return jsonify({"detected_defect": class_name, "remedy": remedy})
65
-
66
- if __name__ == '__main__':
67
- app.run(debug=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from streamlit_extras.colored_header import colored_header
3
+ from streamlit_extras.add_vertical_space import add_vertical_space
4
+ from streamlit_card import card
5
+ import plotly.graph_objects as go
6
+ import streamlit as st
7
  import torch
8
+ from PIL import Image
9
+ import numpy as np
10
+ from transformers import ViTFeatureExtractor, ViTForImageClassification
11
  from sentence_transformers import SentenceTransformer
12
+ import matplotlib.pyplot as plt
13
+ import logging
14
+ import faiss
15
+ from typing import List, Dict
16
+ from datetime import datetime
17
+ from groq import Groq
18
+ import os
19
+ from functools import lru_cache
20
+
21
+ # Setup logging
22
+ logging.basicConfig(level=logging.INFO)
23
+ logger = logging.getLogger(__name__)
24
+
25
+ class RAGSystem:
26
+ def __init__(self):
27
+ # Load models only when needed
28
+ self._embedding_model = None
29
+ self._vector_store = None
30
+ self._knowledge_base = None
31
+
32
+ @property
33
+ def embedding_model(self):
34
+ if self._embedding_model is None:
35
+ self._embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
36
+ return self._embedding_model
37
+
38
+ @property
39
+ def knowledge_base(self):
40
+ if self._knowledge_base is None:
41
+ self._knowledge_base = self.load_knowledge_base()
42
+ return self._knowledge_base
43
+
44
+ @property
45
+ def vector_store(self):
46
+ if self._vector_store is None:
47
+ self._vector_store = self.create_vector_store()
48
+ return self._vector_store
49
+
50
+ @staticmethod
51
+ @lru_cache(maxsize=1) # Cache the knowledge base
52
+ def load_knowledge_base() -> List[Dict]:
53
+ """Load and preprocess knowledge base"""
54
+ kb = {
55
+ "spalling": [
56
+ {
57
+ "severity": "Critical",
58
+ "description": "Severe concrete spalling with exposed reinforcement",
59
+ "repair_method": "Remove deteriorated concrete, clean reinforcement",
60
+ "immediate_action": "Evacuate area, install support",
61
+ "prevention": "Regular inspections, waterproofing"
62
+ }
63
+ ],
64
+ "structural_cracks": [
65
+ {
66
+ "severity": "High",
67
+ "description": "Active structural cracks >5mm width",
68
+ "repair_method": "Structural analysis, epoxy injection",
69
+ "immediate_action": "Install crack monitors",
70
+ "prevention": "Regular monitoring, load management"
71
+ }
72
+ ],
73
+ "surface_deterioration": [
74
+ {
75
+ "severity": "Medium",
76
+ "description": "Surface scaling and deterioration",
77
+ "repair_method": "Surface preparation, patch repair",
78
+ "immediate_action": "Document extent, plan repairs",
79
+ "prevention": "Surface sealers, proper drainage"
80
+ }
81
+ ],
82
+ "corrosion": [
83
+ {
84
+ "severity": "High",
85
+ "description": "Corrosion of reinforcement leading to cracks",
86
+ "repair_method": "Remove rust, apply inhibitors",
87
+ "immediate_action": "Isolate affected area",
88
+ "prevention": "Anti-corrosion coatings, proper drainage"
89
+ }
90
+ ],
91
+ "efflorescence": [
92
+ {
93
+ "severity": "Low",
94
+ "description": "White powder deposits on concrete surfaces",
95
+ "repair_method": "Surface cleaning, sealant application",
96
+ "immediate_action": "Identify moisture source",
97
+ "prevention": "Improve waterproofing, reduce moisture ingress"
98
+ }
99
+ ],
100
+ "delamination": [
101
+ {
102
+ "severity": "Medium",
103
+ "description": "Separation of layers in concrete",
104
+ "repair_method": "Resurface or replace delaminated sections",
105
+ "immediate_action": "Inspect bonding layers",
106
+ "prevention": "Proper curing and bonding agents"
107
+ }
108
+ ],
109
+ "honeycombing": [
110
+ {
111
+ "severity": "Medium",
112
+ "description": "Voids in concrete caused by improper compaction",
113
+ "repair_method": "Grout injection, patch repair",
114
+ "immediate_action": "Assess structural impact",
115
+ "prevention": "Proper vibration during pouring"
116
+ }
117
+ ],
118
+ "water_leakage": [
119
+ {
120
+ "severity": "High",
121
+ "description": "Water ingress through cracks or joints",
122
+ "repair_method": "Injection grouting, waterproofing membranes",
123
+ "immediate_action": "Stop water flow, apply sealants",
124
+ "prevention": "Drainage systems, joint sealing"
125
+ }
126
+ ],
127
+ "settlement_cracks": [
128
+ {
129
+ "severity": "High",
130
+ "description": "Cracks due to uneven foundation settlement",
131
+ "repair_method": "Foundation underpinning, grouting",
132
+ "immediate_action": "Monitor movement, stabilize foundation",
133
+ "prevention": "Soil compaction, proper foundation design"
134
+ }
135
+ ],
136
+ "shrinkage_cracks": [
137
+ {
138
+ "severity": "Low",
139
+ "description": "Minor cracks caused by shrinkage during curing",
140
+ "repair_method": "Sealant application, surface repairs",
141
+ "immediate_action": "Monitor cracks",
142
+ "prevention": "Proper curing and moisture control"
143
+ }
144
+ ]
145
+ }
146
+
147
+ documents = []
148
+ for category, items in kb.items():
149
+ for item in items:
150
+ doc_text = f"Category: {category}\n"
151
+ for key, value in item.items():
152
+ doc_text += f"{key}: {value}\n"
153
+ documents.append({"text": doc_text, "metadata": {"category": category}})
154
+
155
+ return documents
156
+
157
+ def create_vector_store(self):
158
+ """Create FAISS vector store"""
159
+ texts = [doc["text"] for doc in self.knowledge_base]
160
+ embeddings = self.embedding_model.encode(texts)
161
+ dimension = embeddings.shape[1]
162
+ index = faiss.IndexFlatL2(dimension)
163
+ index.add(np.array(embeddings).astype('float32'))
164
+ return index
165
+
166
+ @lru_cache(maxsize=32) # Cache recent query results
167
+ def get_relevant_context(self, query: str, k: int = 2) -> str:
168
+ """Retrieve relevant context based on query"""
169
+ try:
170
+ query_embedding = self.embedding_model.encode([query])
171
+ D, I = self.vector_store.search(np.array(query_embedding).astype('float32'), k)
172
+ context = "\n\n".join([self.knowledge_base[i]["text"] for i in I[0]])
173
+ return context
174
+ except Exception as e:
175
+ logger.error(f"Error retrieving context: {e}")
176
+ return ""
177
+
178
+ class ImageAnalyzer:
179
+ def __init__(self, model_name="microsoft/swin-base-patch4-window7-224-in22k"):
180
+ self.device = "cpu"
181
+ self.defect_classes = ["spalling", "structural_cracks", "surface_deterioration"]
182
+ self.model_name = model_name
183
+ self._model = None
184
+ self._feature_extractor = None
185
+
186
+ @property
187
+ def model(self):
188
+ if self._model is None:
189
+ self._model = self._load_model()
190
+ return self._model
191
+
192
+ @property
193
+ def feature_extractor(self):
194
+ if self._feature_extractor is None:
195
+ self._feature_extractor = self._load_feature_extractor()
196
+ return self._feature_extractor
197
+
198
+ def _load_feature_extractor(self):
199
+ """Load the appropriate feature extractor based on model type"""
200
+ try:
201
+ if "swin" in self.model_name:
202
+ from transformers import AutoFeatureExtractor
203
+ return AutoFeatureExtractor.from_pretrained(self.model_name)
204
+ elif "convnext" in self.model_name:
205
+ from transformers import ConvNextFeatureExtractor
206
+ return ConvNextFeatureExtractor.from_pretrained(self.model_name)
207
+ else:
208
+ from transformers import ViTFeatureExtractor
209
+ return ViTFeatureExtractor.from_pretrained(self.model_name)
210
+ except Exception as e:
211
+ logger.error(f"Feature extractor initialization error: {e}")
212
+ return None
213
+
214
+ def _load_model(self):
215
+ try:
216
+ if "swin" in self.model_name:
217
+ from transformers import SwinForImageClassification
218
+ model = SwinForImageClassification.from_pretrained(
219
+ self.model_name,
220
+ num_labels=len(self.defect_classes),
221
+ ignore_mismatched_sizes=True
222
+ )
223
+ elif "convnext" in self.model_name:
224
+ from transformers import ConvNextForImageClassification
225
+ model = ConvNextForImageClassification.from_pretrained(
226
+ self.model_name,
227
+ num_labels=len(self.defect_classes),
228
+ ignore_mismatched_sizes=True
229
+ )
230
+ else:
231
+ from transformers import ViTForImageClassification
232
+ model = ViTForImageClassification.from_pretrained(
233
+ self.model_name,
234
+ num_labels=len(self.defect_classes),
235
+ ignore_mismatched_sizes=True
236
+ )
237
+
238
+ model = model.to(self.device)
239
+
240
+ # Reinitialize the classifier layer
241
+ with torch.no_grad():
242
+ if hasattr(model, 'classifier'):
243
+ in_features = model.classifier.in_features
244
+ model.classifier = torch.nn.Linear(in_features, len(self.defect_classes))
245
+ elif hasattr(model, 'head'):
246
+ in_features = model.head.in_features
247
+ model.head = torch.nn.Linear(in_features, len(self.defect_classes))
248
+
249
+ return model
250
+ except Exception as e:
251
+ logger.error(f"Model initialization error: {e}")
252
+ return None
253
+
254
+ def preprocess_image(self, image_bytes):
255
+ """Preprocess image for model input"""
256
+ return _cached_preprocess_image(image_bytes, self.model_name)
257
+
258
+ def analyze_image(self, image):
259
+ """Analyze image for defects"""
260
+ try:
261
+ if self.model is None:
262
+ raise ValueError("Model not properly initialized")
263
+
264
+ inputs = self.feature_extractor(
265
+ images=image,
266
+ return_tensors="pt"
267
+ )
268
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
269
+
270
+ with torch.no_grad():
271
+ outputs = self.model(**inputs)
272
+
273
+ probs = torch.nn.functional.softmax(outputs.logits, dim=1)[0]
274
+
275
+ confidence_threshold = 0.3
276
+ results = {
277
+ self.defect_classes[i]: float(probs[i])
278
+ for i in range(len(self.defect_classes))
279
+ if float(probs[i]) > confidence_threshold
280
+ }
281
+
282
+ if not results:
283
+ max_idx = torch.argmax(probs)
284
+ results = {self.defect_classes[int(max_idx)]: float(probs[max_idx])}
285
+
286
+ return results
287
+
288
+ except Exception as e:
289
+ logger.error(f"Analysis error: {str(e)}")
290
+ return None
291
+
292
+ @st.cache_data
293
+ def _cached_preprocess_image(image_bytes, model_name):
294
+ """Cached version of image preprocessing"""
295
+ try:
296
+ image = Image.open(image_bytes)
297
+ if image.mode != 'RGB':
298
+ image = image.convert('RGB')
299
+
300
+ # Adjust size based on model requirements
301
+ if "convnext" in model_name:
302
+ width, height = 384, 384
303
+ else:
304
+ width, height = 224, 224
305
+
306
+ image = image.resize((width, height), Image.Resampling.LANCZOS)
307
+ return image
308
+ except Exception as e:
309
+ logger.error(f"Image preprocessing error: {e}")
310
+ return None
311
+
312
+ @st.cache_data
313
+ def get_groq_response(query: str, context: str) -> str:
314
+ """Get response from Groq LLM with caching"""
315
+ try:
316
+ if not os.getenv("GROQ_API_KEY"):
317
+ return "Error: Groq API key not configured"
318
+
319
+ client = Groq(api_key=os.getenv("GROQ_API_KEY"))
320
+
321
+ prompt = f"""Based on the following context about construction defects, answer the question.
322
+ Context: {context}
323
+ Question: {query}
324
+ Provide a detailed answer based on the given context."""
325
+
326
+ response = client.chat.completions.create(
327
+ messages=[
328
+ {
329
+ "role": "system",
330
+ "content": "You are a construction defect analysis expert."
331
+ },
332
+ {
333
+ "role": "user",
334
+ "content": prompt
335
+ }
336
+ ],
337
+ model="llama-3.3-70b-versatile",
338
+ temperature=0.7,
339
+ )
340
+ return response.choices[0].message.content
341
+ except Exception as e:
342
+ logger.error(f"Groq API error: {e}", exc_info=True)
343
+ return f"Error: Unable to get response from AI model. Exception: {str(e)}"
344
+
345
+
346
+ def create_plotly_confidence_chart(results):
347
+ """Create an interactive confidence chart using Plotly"""
348
+ fig = go.Figure(data=[
349
+ go.Bar(
350
+ x=list(results.values()),
351
+ y=list(results.keys()),
352
+ orientation='h',
353
+ marker_color='rgb(26, 118, 255)',
354
+ text=[f'{v:.1%}' for v in results.values()],
355
+ textposition='auto',
356
+ )
357
+ ])
358
+
359
+ fig.update_layout(
360
+ title='Defect Detection Confidence Levels',
361
+ xaxis_title='Confidence',
362
+ yaxis_title='Defect Type',
363
+ template='plotly_white',
364
+ height=400,
365
+ margin=dict(l=20, r=20, t=40, b=20),
366
+ xaxis=dict(range=[0, 1])
367
+ )
368
+ return fig
369
+
370
+ def create_defect_card(title, description, severity, repair_method):
371
+ """Create a styled card for defect information"""
372
+ severity_colors = {
373
+ "Critical": "red",
374
+ "High": "orange",
375
+ "Medium": "yellow",
376
+ "Low": "green"
377
+ }
378
+
379
+ return f"""
380
+ <div style="border: 1px solid #ddd; border-radius: 10px; padding: 15px; margin: 10px 0;">
381
+ <h3 style="color: #1f77b4; margin: 0 0 10px 0;">{title}</h3>
382
+ <p><strong>Description:</strong> {description}</p>
383
+ <p><strong>Severity:</strong>
384
+ <span style="color: {severity_colors.get(severity, 'gray')}">
385
+ {severity}
386
+ </span>
387
+ </p>
388
+ <p><strong>Repair Method:</strong> {repair_method}</p>
389
+ </div>
390
+ """
391
+
392
+ def main():
393
+ st.set_page_config(
394
+ page_title="Smart Construction Defect Analyzer",
395
+ page_icon="πŸ—οΈ",
396
+ layout="wide",
397
+ initial_sidebar_state="expanded"
398
+ )
399
+
400
+ # Custom CSS
401
+ st.markdown("""
402
+ <style>
403
+ .stApp {
404
+ background-color: #f8f9fa;
405
+ }
406
+ .css-1d391kg {
407
+ padding: 2rem 1rem;
408
+ }
409
+ .stButton>button {
410
+ width: 100%;
411
+ }
412
+ .upload-text {
413
+ text-align: center;
414
+ padding: 2rem;
415
+ border: 2px dashed #ccc;
416
+ border-radius: 10px;
417
+ background-color: #ffffff;
418
+ }
419
+ .info-box {
420
+ background-color: #e9ecef;
421
+ padding: 1rem;
422
+ border-radius: 10px;
423
+ margin: 1rem 0;
424
+ }
425
+ </style>
426
+ """, unsafe_allow_html=True)
427
+
428
+ # Initialize session state
429
+ if 'analyzer' not in st.session_state:
430
+ st.session_state.analyzer = ImageAnalyzer()
431
+ if 'rag_system' not in st.session_state:
432
+ st.session_state.rag_system = RAGSystem()
433
+ if 'analysis_history' not in st.session_state:
434
+ st.session_state.analysis_history = []
435
+
436
+ # Sidebar
437
+ with st.sidebar:
438
+ colored_header(
439
+ label="System Controls",
440
+ description="Settings and Information",
441
+ color_name="blue-70"
442
+ )
443
+
444
+ if os.getenv("GROQ_API_KEY"):
445
+ st.success("🟒 AI System: Connected")
446
+ else:
447
+ st.error("πŸ”΄ AI System: Not configured")
448
+
449
+ add_vertical_space(2)
450
+
451
+ with st.expander("ℹ️ About", expanded=True):
452
+ st.write("""
453
+ ### Smart Construction Defect Analyzer
454
+
455
+ This advanced tool combines computer vision and AI to:
456
+ - Detect construction defects in images
457
+ - Provide detailed repair recommendations
458
+ - Answer technical questions
459
+ - Track analysis history
460
+ """)
461
+
462
+ with st.expander("πŸ”§ Settings"):
463
+ if st.button("Clear Analysis History"):
464
+ st.session_state.analysis_history = []
465
+ st.cache_data.clear()
466
+ st.success("History cleared!")
467
+
468
+ confidence_threshold = st.slider(
469
+ "Detection Confidence Threshold",
470
+ min_value=0.0,
471
+ max_value=1.0,
472
+ value=0.3,
473
+ step=0.1
474
+ )
475
+
476
+ # Main content
477
+ colored_header(
478
+ label="Construction Defect Analyzer",
479
+ description="Upload images and get instant defect analysis",
480
+ color_name="blue-70"
481
+ )
482
+
483
+ tab1, tab2, tab3 = st.tabs(["πŸ“Έ Image Analysis", "❓ Ask Expert", "πŸ“Š Analysis History"])
484
+
485
+ with tab1:
486
+ col1, col2 = st.columns([1, 1])
487
+
488
+ with col1:
489
+ st.markdown('<div class="upload-text">', unsafe_allow_html=True)
490
+ uploaded_file = st.file_uploader(
491
+ "Drop your construction image here",
492
+ type=["jpg", "jpeg", "png"],
493
+ key="image_uploader"
494
+ )
495
+ st.markdown('</div>', unsafe_allow_html=True)
496
+
497
+ if uploaded_file:
498
+ try:
499
+ with st.spinner('Processing image...'):
500
+ processed_image = st.session_state.analyzer.preprocess_image(uploaded_file)
501
+ if processed_image:
502
+ st.image(processed_image, caption='Analyzed Image', use_column_width=True)
503
+
504
+ results = st.session_state.analyzer.analyze_image(processed_image)
505
+ if results:
506
+ # Store analysis in history
507
+ st.session_state.analysis_history.append({
508
+ 'timestamp': datetime.now(),
509
+ 'results': results,
510
+ 'image': processed_image
511
+ })
512
+ except Exception as e:
513
+ st.error(f"Error: {str(e)}")
514
+
515
+ with col2:
516
+ if uploaded_file and results:
517
+ st.markdown("### Analysis Results")
518
+
519
+ # Interactive confidence chart
520
+ fig = create_plotly_confidence_chart(results)
521
+ st.plotly_chart(fig, use_container_width=True)
522
+
523
+ # Most critical defect
524
+ most_likely_defect = max(results.items(), key=lambda x: x[1])[0]
525
+ st.info(f"πŸ” Primary Defect Detected: {most_likely_defect}")
526
+
527
+ # Get detailed information about the defect
528
+ context = st.session_state.rag_system.get_relevant_context(most_likely_defect)
529
+ if context:
530
+ st.markdown("### Defect Details")
531
+ st.markdown(create_defect_card(
532
+ most_likely_defect,
533
+ context.split('\n')[2].split(': ')[1],
534
+ context.split('\n')[1].split(': ')[1],
535
+ context.split('\n')[3].split(': ')[1]
536
+ ), unsafe_allow_html=True)
537
+
538
+ with tab2:
539
+ st.markdown("### Ask the Construction Expert")
540
+
541
+ query_placeholder = "Example: What are the best repair methods for structural cracks?"
542
+ user_query = st.text_input("Your Question:", placeholder=query_placeholder)
543
+
544
+ if user_query:
545
+ with st.spinner('Consulting AI expert...'):
546
+ context = st.session_state.rag_system.get_relevant_context(user_query)
547
+ if context:
548
+ response = get_groq_response(user_query, context)
549
+
550
+ if not response.startswith("Error"):
551
+ st.markdown("### Expert Response")
552
+ st.markdown(response)
553
+
554
+ with st.expander("View Source Information"):
555
+ st.markdown(context)
556
+ else:
557
+ st.error(response)
558
+
559
+ with tab3:
560
+ if st.session_state.analysis_history:
561
+ for i, analysis in enumerate(reversed(st.session_state.analysis_history)):
562
+ with st.expander(f"Analysis {i+1} - {analysis['timestamp'].strftime('%Y-%m-%d %H:%M')}"):
563
+ col1, col2 = st.columns([1, 1])
564
+ with col1:
565
+ st.image(analysis['image'], caption='Analyzed Image', use_column_width=True)
566
+ with col2:
567
+ fig = create_plotly_confidence_chart(analysis['results'])
568
+ st.plotly_chart(fig, use_container_width=True)
569
+ else:
570
+ st.info("No analysis history available")
571
+
572
+ if __name__ == "__main__":
573
+ main()