ashhal commited on
Commit
cff6a7e
·
verified ·
1 Parent(s): 666dace

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +671 -375
app.py CHANGED
@@ -2,101 +2,209 @@ import gradio as gr
2
  import os
3
  import json
4
  import time
 
5
  from datetime import datetime
6
  from groq import Groq
7
  import pandas as pd
8
  from typing import Dict, List, Tuple, Optional
9
  import re
10
- import uuid
 
 
 
11
 
12
- # Initialize Groq client
13
  def get_groq_client():
14
  api_key = os.getenv("GROQ_API_KEY")
15
  if not api_key:
16
  raise ValueError("GROQ_API_KEY environment variable not set")
17
  return Groq(api_key=api_key)
18
 
19
- class PowerSystemsConsultant:
20
  def __init__(self):
21
- self.groq_client = get_groq_client()
22
- self.knowledge_base = self.load_knowledge_base()
23
- self.conversation_history = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
- def load_knowledge_base(self):
26
- """Load the power systems knowledge base"""
 
 
 
 
 
 
 
27
  try:
28
- with open('data/knowledge_base.json', 'r', encoding='utf-8') as f:
29
- return json.load(f)
30
- except FileNotFoundError:
31
- return self.get_default_knowledge_base()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
- def get_default_knowledge_base(self):
34
- """Default knowledge base with power systems content"""
35
- return {
36
- "faults": {
37
- "symmetrical_faults": {
38
- "description": "Three-phase faults where all phases are equally affected",
39
- "characteristics": "Balanced conditions, easiest to analyze",
40
- "analysis_method": "Single-phase equivalent circuit"
41
- },
42
- "unsymmetrical_faults": {
43
- "line_to_ground": "Most common fault (70-80% of all faults)",
44
- "line_to_line": "Second most common fault (15-20%)",
45
- "double_line_to_ground": "Less common but severe"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
- },
48
- "protection": {
49
- "overcurrent_protection": "Time-current characteristic curves, coordination",
50
- "differential_protection": "Used for transformers, generators, buses",
51
- "distance_protection": "Impedance-based protection for transmission lines"
52
- },
53
- "standards": {
54
- "IEEE_standards": ["IEEE C37", "IEEE 1547", "IEEE 519"],
55
- "IEC_standards": ["IEC 61850", "IEC 60909", "IEC 61131"]
56
- },
57
- "diagrams": {
58
- "single_line_diagram": "Shows electrical connections and components",
59
- "protection_coordination": "Time-current curves for protective devices",
60
- "fault_analysis": "Sequence networks for fault calculations"
61
- }
62
- }
63
 
64
- def retrieve_relevant_context(self, query: str) -> str:
65
- """Simple keyword-based retrieval from knowledge base"""
66
- query_lower = query.lower()
67
- relevant_info = []
68
-
69
- # Search through knowledge base
70
- for category, content in self.knowledge_base.items():
71
- if any(keyword in query_lower for keyword in [category]):
72
- if isinstance(content, dict):
73
- for key, value in content.items():
74
- if any(keyword in query_lower for keyword in key.split('_')):
75
- relevant_info.append(f"{category.title()} - {key}: {value}")
76
- else:
77
- relevant_info.append(f"{category.title()}: {content}")
78
-
79
- return "\n".join(relevant_info[:5]) # Limit to top 5 matches
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
- def generate_response(self, user_query: str, chat_history: List[Tuple[str, str]]) -> str:
82
  """Generate response using Groq LLM with RAG context"""
83
  try:
 
 
 
 
 
 
 
84
  # Retrieve relevant context
85
- context = self.retrieve_relevant_context(user_query)
86
 
87
  # Prepare system prompt
88
  system_prompt = f"""You are a Power Systems Mini-Consultant AI assistant specializing in electrical power systems.
89
  You help with:
90
  1. Fault analysis and calculations
91
  2. Protection system design
92
- 3. Standards interpretation
93
  4. Study materials and exam preparation
94
  5. Practice problem generation
 
95
 
96
  Use the following context from the knowledge base:
97
  {context}
98
 
99
- Provide clear, technical explanations with practical examples. Include relevant formulas, standards references, and safety considerations when appropriate."""
 
 
 
 
 
 
 
 
100
 
101
  # Prepare conversation context
102
  messages = [{"role": "system", "content": system_prompt}]
@@ -117,58 +225,147 @@ class PowerSystemsConsultant:
117
  temperature=0.8
118
  )
119
 
120
- return response.choices[0].message.content
 
 
 
 
 
 
 
121
 
122
  except Exception as e:
123
- return f"Error generating response: {str(e)}. Please check your GROQ_API_KEY and try again."
124
-
125
- def generate_practice_pack(self, topic: str, difficulty: str, num_questions: int) -> str:
126
- """Generate practice questions for power systems topics"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  try:
128
- prompt = f"""Generate {num_questions} practice questions about {topic} in power systems
129
- with {difficulty} difficulty level. Include:
130
 
131
- 1. Multiple choice questions with 4 options
132
- 2. Short answer questions
133
- 3. Calculation problems with given data
134
- 4. Provide answers and brief explanations
135
 
136
- Format as a structured practice pack suitable for exam preparation."""
 
137
 
138
- response = self.groq_client.chat.completions.create(
139
- model="llama3-70b-8192",
140
- messages=[{"role": "user", "content": prompt}],
141
- max_tokens=2000,
142
- temperature=0.8
143
- )
144
 
145
- return response.choices[0].message.content
146
  except Exception as e:
147
- return f"Error generating practice pack: {str(e)}"
148
-
149
- def explain_standard(self, standard_name: str) -> str:
150
- """Explain power system standards"""
151
  try:
152
- prompt = f"""Provide a comprehensive explanation of the {standard_name} standard in power systems.
153
- Include:
154
- 1. Purpose and scope
155
- 2. Key requirements
156
- 3. Applications
157
- 4. Related standards
158
- 5. Practical implementation notes
159
 
160
- Keep it technically accurate but accessible."""
 
 
 
161
 
162
- response = self.groq_client.chat.completions.create(
163
- model="llama3-70b-8192",
164
- messages=[{"role": "user", "content": prompt}],
165
- max_tokens=2000,
166
- temperature=0.8
167
  )
168
 
169
- return response.choices[0].message.content
 
 
170
  except Exception as e:
171
- return f"Error explaining standard: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  # Initialize the consultant
174
  try:
@@ -178,312 +375,411 @@ except Exception as e:
178
  consultant = None
179
  initialization_status = f"❌ Initialization failed: {str(e)}"
180
 
181
- def chat_interface(message, history):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  """Main chat interface function"""
183
- if consultant is None:
184
- return history + [["", "❌ System not initialized. Please check GROQ_API_KEY."]]
 
 
 
185
 
186
  if not message.strip():
187
- return history + [[message, "Please enter a question about power systems."]]
188
 
189
  try:
190
- response = consultant.generate_response(message, history)
191
- history = history + [[message, response]]
192
- return history
 
 
 
 
 
193
  except Exception as e:
194
- return history + [[message, f"Error: {str(e)}"]]
195
 
196
- def generate_practice_questions(topic, difficulty, num_questions):
197
  """Generate practice pack interface"""
198
- if consultant is None:
199
  return "❌ System not initialized. Please check GROQ_API_KEY."
200
 
 
 
 
201
  try:
202
  practice_pack = consultant.generate_practice_pack(topic, difficulty, int(num_questions))
203
  return practice_pack
204
  except Exception as e:
205
  return f"Error generating practice pack: {str(e)}"
206
 
207
- def explain_standard_interface(standard):
208
  """Standards explanation interface"""
209
- if consultant is None:
210
  return "❌ System not initialized. Please check GROQ_API_KEY."
211
 
 
 
 
212
  try:
213
  explanation = consultant.explain_standard(standard)
214
  return explanation
215
  except Exception as e:
216
  return f"Error explaining standard: {str(e)}"
217
 
218
- # Create Gradio interface
219
- with gr.Blocks(
220
- theme=gr.themes.Soft(
221
- primary_hue="blue",
222
- secondary_hue="cyan",
223
- neutral_hue="slate"
224
- ),
225
- title="Power Systems Mini-Consultant",
226
- css="""
227
- .header-text {
228
- text-align: center;
229
- font-size: 2.5em;
230
- font-weight: bold;
231
- color: #1f4e79;
232
- margin-bottom: 0.5em;
233
- }
234
- .subtitle-text {
235
- text-align: center;
236
- font-size: 1.2em;
237
- color: #666;
238
- margin-bottom: 2em;
239
- }
240
- .status-box {
241
- padding: 1em;
242
- border-radius: 8px;
243
- margin: 1em 0;
244
- }
245
- .feature-box {
246
- border: 2px solid #e0e7ff;
247
- border-radius: 12px;
248
- padding: 1.5em;
249
- margin: 1em 0;
250
- background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
251
- }
252
- """
253
- ) as app:
254
-
255
- # Header
256
- gr.HTML("""
257
- <div class="header-text">⚡ Power Systems Mini-Consultant</div>
258
- <div class="subtitle-text">
259
- Advanced AI Assistant for Fault Analysis, Protection Systems & Standards
260
- </div>
261
- """)
262
-
263
- # Status display
264
- gr.HTML(f"""
265
- <div class="status-box" style="background-color: {'#d4edda' if '✅' in initialization_status else '#f8d7da'};
266
- color: {'#155724' if '✅' in initialization_status else '#721c24'};">
267
- <strong>System Status:</strong> {initialization_status}
268
- </div>
269
- """)
270
-
271
- with gr.Tabs():
272
- # Main Chat Tab
273
- with gr.TabItem("💬 AI Consultant Chat"):
274
- gr.HTML("""
275
- <div class="feature-box">
276
- <h3>🤖 Intelligent Power Systems Consultant</h3>
277
- <p>Ask questions about fault analysis, protection systems, standards, calculations, and more!</p>
278
- <ul>
279
- <li>Fault analysis and calculations</li>
280
- <li>Protection system design</li>
281
- <li>Standards interpretation (IEEE, IEC)</li>
282
- <li>Study guidance and explanations</li>
283
- </ul>
284
- </div>
285
- """)
286
-
287
- chatbot = gr.Chatbot(
288
- height=400,
289
- placeholder="Your Power Systems AI Consultant will appear here...",
290
- show_label=False
291
- )
292
-
293
- msg = gr.Textbox(
294
- placeholder="Ask about faults, protection, standards, calculations...",
295
- show_label=False,
296
- container=False
297
- )
298
-
299
- with gr.Row():
300
- submit_btn = gr.Button("Send 📤", variant="primary")
301
- clear_btn = gr.Button("Clear Chat 🗑️", variant="secondary")
302
-
303
- # Example questions
304
- gr.HTML("""
305
- <div class="feature-box">
306
- <h4>💡 Example Questions:</h4>
307
- <ul>
308
- <li>"Explain symmetrical vs unsymmetrical faults"</li>
309
- <li>"How to calculate short circuit current?"</li>
310
- <li>"What is IEEE C37 standard about?"</li>
311
- <li>"Design overcurrent protection for a 33kV feeder"</li>
312
- </ul>
313
- </div>
314
- """)
315
-
316
- submit_btn.click(
317
- chat_interface,
318
- inputs=[msg, chatbot],
319
- outputs=[chatbot]
320
- ).then(
321
- lambda: "",
322
- outputs=[msg]
323
- )
324
-
325
- msg.submit(
326
- chat_interface,
327
- inputs=[msg, chatbot],
328
- outputs=[chatbot]
329
- ).then(
330
- lambda: "",
331
- outputs=[msg]
332
- )
333
-
334
- clear_btn.click(lambda: None, outputs=[chatbot])
335
 
336
- # Practice Pack Generator
337
- with gr.TabItem("📚 Practice Pack Generator"):
338
- gr.HTML("""
339
- <div class="feature-box">
340
- <h3>📝 AI-Generated Practice Questions</h3>
341
- <p>Generate customized practice packs for power systems topics with varying difficulty levels.</p>
342
- </div>
343
- """)
344
-
345
- with gr.Row():
346
- with gr.Column(scale=1):
347
- topic_input = gr.Dropdown(
348
- choices=[
349
- "Fault Analysis", "Protection Systems", "Power Flow",
350
- "Stability Studies", "Relay Coordination", "Transformers",
351
- "Transmission Lines", "Load Flow", "Short Circuit Analysis",
352
- "Harmonics", "Power Quality", "SCADA Systems"
353
- ],
354
- label="📋 Select Topic",
355
- value="Fault Analysis"
356
- )
357
-
358
- difficulty_input = gr.Radio(
359
- choices=["Beginner", "Intermediate", "Advanced"],
360
- label="🎯 Difficulty Level",
361
- value="Intermediate"
362
- )
363
-
364
- num_questions_input = gr.Slider(
365
- minimum=5,
366
- maximum=20,
367
- step=1,
368
- value=10,
369
- label="🔢 Number of Questions"
370
- )
371
-
372
- generate_btn = gr.Button("Generate Practice Pack 🎓", variant="primary")
373
-
374
- with gr.Column(scale=2):
375
- practice_output = gr.Textbox(
376
- label="📖 Generated Practice Pack",
377
- lines=20,
378
- placeholder="Your practice questions will appear here..."
379
- )
380
-
381
- generate_btn.click(
382
- generate_practice_questions,
383
- inputs=[topic_input, difficulty_input, num_questions_input],
384
- outputs=[practice_output]
385
- )
386
 
387
- # Standards Explorer
388
- with gr.TabItem("📋 Standards Explorer"):
389
- gr.HTML("""
390
- <div class="feature-box">
391
- <h3>📚 Power Systems Standards Guide</h3>
392
- <p>Get detailed explanations of IEEE, IEC, and other international standards.</p>
393
- </div>
394
- """)
395
-
396
- with gr.Row():
397
- with gr.Column(scale=1):
398
- standard_input = gr.Dropdown(
399
- choices=[
400
- "IEEE C37.2", "IEEE 1547", "IEEE 519", "IEEE C57.12.00",
401
- "IEC 61850", "IEC 60909", "IEC 61131", "IEC 60255",
402
- "ANSI C84.1", "IEEE 242", "IEEE 399", "IEEE C37.010"
403
- ],
404
- label="📑 Select Standard",
405
- value="IEEE C37.2"
406
- )
407
-
408
- explain_btn = gr.Button("Explain Standard 📖", variant="primary")
409
 
410
- with gr.Column(scale=2):
411
- standard_output = gr.Textbox(
412
- label="📋 Standard Explanation",
413
- lines=15,
414
- placeholder="Standard explanation will appear here..."
415
- )
416
-
417
- explain_btn.click(
418
- explain_standard_interface,
419
- inputs=[standard_input],
420
- outputs=[standard_output]
421
- )
 
 
 
 
 
422
 
423
- # Study Resources
424
- with gr.TabItem("📚 Study Resources"):
425
- gr.HTML("""
426
- <div class="feature-box">
427
- <h3>📖 Power Systems Study Guide</h3>
428
- <p>Essential formulas, concepts, and quick reference materials.</p>
429
- </div>
430
- """)
431
-
432
- with gr.Accordion("⚡ Fault Analysis Formulas", open=False):
433
- gr.HTML("""
434
- <h4>Three-Phase Fault Current:</h4>
435
- <p><code>I_fault = V_nominal / Z_total</code></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
 
437
- <h4>Line-to-Ground Fault:</h4>
438
- <p><code>I_fault = 3 * V_nominal / (Z1 + Z2 + Z0)</code></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
 
440
- <h4>Line-to-Line Fault:</h4>
441
- <p><code>I_fault = √3 * V_nominal / (Z1 + Z2)</code></p>
442
- """)
443
-
444
- with gr.Accordion("🛡️ Protection Principles", open=False):
445
- gr.HTML("""
446
- <h4>Overcurrent Protection:</h4>
447
- <ul>
448
- <li>Inverse Time Characteristics</li>
449
- <li>Coordination Time Intervals</li>
450
- <li>Pickup Current Settings</li>
451
- </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
- <h4>Distance Protection:</h4>
454
- <ul>
455
- <li>Zone 1: 80-90% of line length</li>
456
- <li>Zone 2: 120% of protected line + 50% of next line</li>
457
- <li>Zone 3: Backup protection</li>
458
- </ul>
459
- """)
460
-
461
- with gr.Accordion("📊 Key Standards Summary", open=False):
462
- gr.HTML("""
463
- <h4>IEEE Standards:</h4>
464
- <ul>
465
- <li><strong>IEEE C37:</strong> Circuit breakers and switchgear</li>
466
- <li><strong>IEEE 1547:</strong> Distributed energy resources</li>
467
- <li><strong>IEEE 519:</strong> Harmonic control</li>
468
- <li><strong>IEEE 242:</strong> Protection and coordination</li>
469
- </ul>
470
 
471
- <h4>IEC Standards:</h4>
472
- <ul>
473
- <li><strong>IEC 61850:</strong> Substation automation</li>
474
- <li><strong>IEC 60909:</strong> Short-circuit calculations</li>
475
- <li><strong>IEC 60255:</strong> Protective relays</li>
476
- </ul>
477
- """)
478
-
479
- # Footer
480
- gr.HTML("""
481
- <div style="text-align: center; margin-top: 2em; padding: 1em; background-color: #f8f9fa; border-radius: 8px;">
482
- <p><strong>⚡ Power Systems Mini-Consultant</strong></p>
483
- <p>Built with Groq AI • Deployed on Hugging Face Spaces</p>
484
- <p><em>For educational and professional use in power systems engineering</em></p>
485
- </div>
486
- """)
487
-
488
- if __name__ == "__main__":
489
- app.launch()
 
2
  import os
3
  import json
4
  import time
5
+ import uuid
6
  from datetime import datetime
7
  from groq import Groq
8
  import pandas as pd
9
  from typing import Dict, List, Tuple, Optional
10
  import re
11
+ import hashlib
12
+ import sqlite3
13
+ from diagram_generator import DiagramGenerator
14
+ from rag_system import EnhancedRAGSystem
15
 
16
+ # Initialize components
17
  def get_groq_client():
18
  api_key = os.getenv("GROQ_API_KEY")
19
  if not api_key:
20
  raise ValueError("GROQ_API_KEY environment variable not set")
21
  return Groq(api_key=api_key)
22
 
23
+ class UserManager:
24
  def __init__(self):
25
+ self.db_path = 'users.db'
26
+ self.init_database()
27
+
28
+ def init_database(self):
29
+ """Initialize SQLite database for users"""
30
+ conn = sqlite3.connect(self.db_path)
31
+ cursor = conn.cursor()
32
+
33
+ cursor.execute('''
34
+ CREATE TABLE IF NOT EXISTS users (
35
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
36
+ username TEXT UNIQUE NOT NULL,
37
+ email TEXT UNIQUE NOT NULL,
38
+ password_hash TEXT NOT NULL,
39
+ full_name TEXT,
40
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
41
+ last_login TIMESTAMP
42
+ )
43
+ ''')
44
+
45
+ cursor.execute('''
46
+ CREATE TABLE IF NOT EXISTS chat_sessions (
47
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
48
+ user_id INTEGER,
49
+ session_id TEXT UNIQUE,
50
+ title TEXT,
51
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
52
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
53
+ FOREIGN KEY (user_id) REFERENCES users (id)
54
+ )
55
+ ''')
56
+
57
+ cursor.execute('''
58
+ CREATE TABLE IF NOT EXISTS messages (
59
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
60
+ session_id TEXT,
61
+ role TEXT,
62
+ content TEXT,
63
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
64
+ FOREIGN KEY (session_id) REFERENCES chat_sessions (session_id)
65
+ )
66
+ ''')
67
 
68
+ conn.commit()
69
+ conn.close()
70
+
71
+ def hash_password(self, password: str) -> str:
72
+ """Hash password using SHA-256"""
73
+ return hashlib.sha256(password.encode()).hexdigest()
74
+
75
+ def create_user(self, username: str, email: str, password: str, full_name: str = "") -> Tuple[bool, str]:
76
+ """Create new user account"""
77
  try:
78
+ conn = sqlite3.connect(self.db_path)
79
+ cursor = conn.cursor()
80
+
81
+ password_hash = self.hash_password(password)
82
+ cursor.execute(
83
+ "INSERT INTO users (username, email, password_hash, full_name) VALUES (?, ?, ?, ?)",
84
+ (username, email, password_hash, full_name)
85
+ )
86
+
87
+ conn.commit()
88
+ conn.close()
89
+ return True, "Account created successfully!"
90
+
91
+ except sqlite3.IntegrityError as e:
92
+ if "username" in str(e):
93
+ return False, "Username already exists"
94
+ elif "email" in str(e):
95
+ return False, "Email already registered"
96
+ else:
97
+ return False, "Registration failed"
98
+ except Exception as e:
99
+ return False, f"Error: {str(e)}"
100
 
101
+ def authenticate_user(self, username: str, password: str) -> Tuple[bool, Optional[Dict]]:
102
+ """Authenticate user login"""
103
+ try:
104
+ conn = sqlite3.connect(self.db_path)
105
+ cursor = conn.cursor()
106
+
107
+ password_hash = self.hash_password(password)
108
+ cursor.execute(
109
+ "SELECT id, username, email, full_name FROM users WHERE username = ? AND password_hash = ?",
110
+ (username, password_hash)
111
+ )
112
+
113
+ user = cursor.fetchone()
114
+
115
+ if user:
116
+ # Update last login
117
+ cursor.execute(
118
+ "UPDATE users SET last_login = CURRENT_TIMESTAMP WHERE id = ?",
119
+ (user[0],)
120
+ )
121
+ conn.commit()
122
+
123
+ user_data = {
124
+ 'id': user[0],
125
+ 'username': user[1],
126
+ 'email': user[2],
127
+ 'full_name': user[3] or user[1]
128
  }
129
+ conn.close()
130
+ return True, user_data
131
+
132
+ conn.close()
133
+ return False, None
134
+
135
+ except Exception as e:
136
+ return False, None
 
 
 
 
 
 
 
 
137
 
138
+ def get_user_sessions(self, user_id: int) -> List[Dict]:
139
+ """Get chat sessions for user"""
140
+ try:
141
+ conn = sqlite3.connect(self.db_path)
142
+ cursor = conn.cursor()
143
+
144
+ cursor.execute(
145
+ "SELECT session_id, title, created_at FROM chat_sessions WHERE user_id = ? ORDER BY updated_at DESC",
146
+ (user_id,)
147
+ )
148
+
149
+ sessions = cursor.fetchall()
150
+ conn.close()
151
+
152
+ return [
153
+ {
154
+ 'session_id': session[0],
155
+ 'title': session[1] or f"Chat {session[0][:8]}",
156
+ 'created_at': session[2]
157
+ }
158
+ for session in sessions
159
+ ]
160
+
161
+ except Exception as e:
162
+ return []
163
+
164
+ class PowerSystemsConsultant:
165
+ def __init__(self):
166
+ self.groq_client = get_groq_client()
167
+ self.rag_system = EnhancedRAGSystem()
168
+ self.diagram_generator = DiagramGenerator()
169
+ self.user_manager = UserManager()
170
+ self.current_user = None
171
+ self.current_session = None
172
 
173
+ def generate_response(self, user_query: str, chat_history: List[Tuple[str, str]], session_id: str = None) -> Tuple[str, str]:
174
  """Generate response using Groq LLM with RAG context"""
175
  try:
176
+ # Check if query is asking for a diagram
177
+ diagram_type = self.detect_diagram_request(user_query)
178
+ diagram_svg = None
179
+
180
+ if diagram_type:
181
+ diagram_svg = self.generate_diagram(diagram_type, user_query)
182
+
183
  # Retrieve relevant context
184
+ context = self.rag_system.retrieve_context(user_query)
185
 
186
  # Prepare system prompt
187
  system_prompt = f"""You are a Power Systems Mini-Consultant AI assistant specializing in electrical power systems.
188
  You help with:
189
  1. Fault analysis and calculations
190
  2. Protection system design
191
+ 3. Standards interpretation (IEEE, IEC, ANSI)
192
  4. Study materials and exam preparation
193
  5. Practice problem generation
194
+ 6. Engineering diagrams and visualizations
195
 
196
  Use the following context from the knowledge base:
197
  {context}
198
 
199
+ Provide clear, technical explanations with practical examples. Include relevant formulas, standards references, and safety considerations when appropriate.
200
+
201
+ If a user asks for diagrams or visual representations, mention that you can generate professional engineering diagrams including:
202
+ - Single line diagrams
203
+ - Fault analysis diagrams
204
+ - Protection coordination curves
205
+ - Phasor diagrams
206
+ - Impedance diagrams
207
+ """
208
 
209
  # Prepare conversation context
210
  messages = [{"role": "system", "content": system_prompt}]
 
225
  temperature=0.8
226
  )
227
 
228
+ text_response = response.choices[0].message.content
229
+
230
+ # Save to database if session exists
231
+ if session_id and self.current_user:
232
+ self.save_message(session_id, "user", user_query)
233
+ self.save_message(session_id, "assistant", text_response)
234
+
235
+ return text_response, diagram_svg
236
 
237
  except Exception as e:
238
+ error_msg = f"Error generating response: {str(e)}. Please check your GROQ_API_KEY and try again."
239
+ return error_msg, None
240
+
241
+ def detect_diagram_request(self, query: str) -> Optional[str]:
242
+ """Detect if user is requesting a diagram"""
243
+ query_lower = query.lower()
244
+
245
+ diagram_keywords = {
246
+ 'single line': 'single_line',
247
+ 'single-line': 'single_line',
248
+ 'fault analysis': 'fault_analysis',
249
+ 'fault diagram': 'fault_analysis',
250
+ 'protection coordination': 'protection_coordination',
251
+ 'coordination curve': 'protection_coordination',
252
+ 'phasor diagram': 'phasor',
253
+ 'phasor': 'phasor',
254
+ 'impedance diagram': 'impedance',
255
+ 'distance protection': 'impedance',
256
+ 'r-x diagram': 'impedance'
257
+ }
258
+
259
+ for keyword, diagram_type in diagram_keywords.items():
260
+ if keyword in query_lower:
261
+ return diagram_type
262
+
263
+ return None
264
+
265
+ def generate_diagram(self, diagram_type: str, query: str) -> str:
266
+ """Generate appropriate diagram based on type"""
267
+ try:
268
+ if diagram_type == 'single_line':
269
+ return self.diagram_generator.generate_single_line_diagram({})
270
+ elif diagram_type == 'fault_analysis':
271
+ fault_type = 'line_to_ground'
272
+ if 'line to line' in query.lower() or 'l-l' in query.lower():
273
+ fault_type = 'line_to_line'
274
+ elif 'three phase' in query.lower() or '3-phase' in query.lower():
275
+ fault_type = 'three_phase'
276
+ return self.diagram_generator.generate_fault_analysis_diagram(fault_type)
277
+ elif diagram_type == 'protection_coordination':
278
+ return self.diagram_generator.generate_protection_coordination_diagram()
279
+ elif diagram_type == 'phasor':
280
+ condition = 'balanced'
281
+ if 'unbalanced' in query.lower() or 'fault' in query.lower():
282
+ condition = 'unbalanced'
283
+ return self.diagram_generator.generate_phasor_diagram(condition)
284
+ elif diagram_type == 'impedance':
285
+ return self.diagram_generator.generate_impedance_diagram()
286
+ except Exception as e:
287
+ return f"<text>Error generating diagram: {str(e)}</text>"
288
+
289
+ return None
290
+
291
+ def create_chat_session(self, user_id: int, title: str = None) -> str:
292
+ """Create new chat session"""
293
+ session_id = str(uuid.uuid4())
294
+
295
  try:
296
+ conn = sqlite3.connect(self.user_manager.db_path)
297
+ cursor = conn.cursor()
298
 
299
+ cursor.execute(
300
+ "INSERT INTO chat_sessions (user_id, session_id, title) VALUES (?, ?, ?)",
301
+ (user_id, session_id, title or f"Chat {datetime.now().strftime('%m/%d %H:%M')}")
302
+ )
303
 
304
+ conn.commit()
305
+ conn.close()
306
 
307
+ return session_id
 
 
 
 
 
308
 
 
309
  except Exception as e:
310
+ return str(uuid.uuid4()) # Fallback to temporary session
311
+
312
+ def save_message(self, session_id: str, role: str, content: str):
313
+ """Save message to database"""
314
  try:
315
+ conn = sqlite3.connect(self.user_manager.db_path)
316
+ cursor = conn.cursor()
 
 
 
 
 
317
 
318
+ cursor.execute(
319
+ "INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)",
320
+ (session_id, role, content)
321
+ )
322
 
323
+ # Update session timestamp
324
+ cursor.execute(
325
+ "UPDATE chat_sessions SET updated_at = CURRENT_TIMESTAMP WHERE session_id = ?",
326
+ (session_id,)
 
327
  )
328
 
329
+ conn.commit()
330
+ conn.close()
331
+
332
  except Exception as e:
333
+ pass # Continue without saving if database error
334
+
335
+ def load_chat_history(self, session_id: str) -> List[Tuple[str, str]]:
336
+ """Load chat history from database"""
337
+ try:
338
+ conn = sqlite3.connect(self.user_manager.db_path)
339
+ cursor = conn.cursor()
340
+
341
+ cursor.execute(
342
+ "SELECT role, content FROM messages WHERE session_id = ? ORDER BY timestamp",
343
+ (session_id,)
344
+ )
345
+
346
+ messages = cursor.fetchall()
347
+ conn.close()
348
+
349
+ # Convert to chat history format
350
+ history = []
351
+ current_pair = [None, None]
352
+
353
+ for role, content in messages:
354
+ if role == "user":
355
+ if current_pair[0] is not None:
356
+ history.append((current_pair[0], current_pair[1] or ""))
357
+ current_pair = [content, None]
358
+ elif role == "assistant":
359
+ current_pair[1] = content
360
+
361
+ # Add the last pair if complete
362
+ if current_pair[0] is not None:
363
+ history.append((current_pair[0], current_pair[1] or ""))
364
+
365
+ return history
366
+
367
+ except Exception as e:
368
+ return []
369
 
370
  # Initialize the consultant
371
  try:
 
375
  consultant = None
376
  initialization_status = f"❌ Initialization failed: {str(e)}"
377
 
378
+ # Global state management
379
+ current_user = gr.State(None)
380
+ current_session = gr.State(None)
381
+ chat_sessions = gr.State([])
382
+
383
+ # UI Functions
384
+ def login_user(username, password):
385
+ """Handle user login"""
386
+ if not consultant:
387
+ return False, None, "System not initialized", gr.update(), gr.update()
388
+
389
+ success, user_data = consultant.user_manager.authenticate_user(username, password)
390
+
391
+ if success:
392
+ consultant.current_user = user_data
393
+ sessions = consultant.user_manager.get_user_sessions(user_data['id'])
394
+
395
+ # Create session list for sidebar
396
+ session_choices = [(f"{s['title']} ({s['created_at'][:10]})", s['session_id']) for s in sessions]
397
+
398
+ return (
399
+ True,
400
+ user_data,
401
+ f"Welcome back, {user_data['full_name']}!",
402
+ gr.update(choices=session_choices, value=session_choices[0][1] if session_choices else None),
403
+ gr.update(visible=True)
404
+ )
405
+ else:
406
+ return False, None, "Invalid username or password", gr.update(), gr.update()
407
+
408
+ def register_user(username, email, password, confirm_password, full_name):
409
+ """Handle user registration"""
410
+ if not consultant:
411
+ return False, "System not initialized"
412
+
413
+ if password != confirm_password:
414
+ return False, "Passwords do not match"
415
+
416
+ if len(password) < 6:
417
+ return False, "Password must be at least 6 characters"
418
+
419
+ success, message = consultant.user_manager.create_user(username, email, password, full_name)
420
+ return success, message
421
+
422
+ def start_new_chat(user_data):
423
+ """Start a new chat session"""
424
+ if not consultant or not user_data:
425
+ return None, []
426
+
427
+ session_id = consultant.create_chat_session(user_data['id'])
428
+ consultant.current_session = session_id
429
+
430
+ # Update sessions list
431
+ sessions = consultant.user_manager.get_user_sessions(user_data['id'])
432
+ session_choices = [(f"{s['title']} ({s['created_at'][:10]})", s['session_id']) for s in sessions]
433
+
434
+ return session_id, session_choices
435
+
436
+ def load_selected_chat(session_id, user_data):
437
+ """Load selected chat session"""
438
+ if not consultant or not session_id:
439
+ return []
440
+
441
+ consultant.current_session = session_id
442
+ history = consultant.load_chat_history(session_id)
443
+
444
+ return history
445
+
446
+ def chat_interface(message, history, user_data, session_id):
447
  """Main chat interface function"""
448
+ if not consultant:
449
+ return history + [["", "❌ System not initialized. Please check GROQ_API_KEY."]], "", None
450
+
451
+ if not user_data:
452
+ return history + [["", "Please log in to continue."]], "", None
453
 
454
  if not message.strip():
455
+ return history + [[message, "Please enter a question about power systems."]], "", None
456
 
457
  try:
458
+ # Generate response and potential diagram
459
+ response, diagram_svg = consultant.generate_response(message, history, session_id)
460
+
461
+ # Update history
462
+ new_history = history + [[message, response]]
463
+
464
+ return new_history, "", diagram_svg
465
+
466
  except Exception as e:
467
+ return history + [[message, f"Error: {str(e)}"]], "", None
468
 
469
+ def generate_practice_questions(topic, difficulty, num_questions, user_data):
470
  """Generate practice pack interface"""
471
+ if not consultant:
472
  return "❌ System not initialized. Please check GROQ_API_KEY."
473
 
474
+ if not user_data:
475
+ return "Please log in to access this feature."
476
+
477
  try:
478
  practice_pack = consultant.generate_practice_pack(topic, difficulty, int(num_questions))
479
  return practice_pack
480
  except Exception as e:
481
  return f"Error generating practice pack: {str(e)}"
482
 
483
+ def explain_standard_interface(standard, user_data):
484
  """Standards explanation interface"""
485
+ if not consultant:
486
  return "❌ System not initialized. Please check GROQ_API_KEY."
487
 
488
+ if not user_data:
489
+ return "Please log in to access this feature."
490
+
491
  try:
492
  explanation = consultant.explain_standard(standard)
493
  return explanation
494
  except Exception as e:
495
  return f"Error explaining standard: {str(e)}"
496
 
497
+ # Create the main application interface
498
+ def create_app():
499
+ with gr.Blocks(
500
+ theme=gr.themes.Soft(
501
+ primary_hue="blue",
502
+ secondary_hue="cyan",
503
+ neutral_hue="slate"
504
+ ),
505
+ title="Power Systems Mini-Consultant",
506
+ css="""
507
+ .header-container {
508
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
509
+ padding: 2rem;
510
+ border-radius: 15px;
511
+ margin-bottom: 2rem;
512
+ text-align: center;
513
+ }
514
+ .header-text {
515
+ color: white;
516
+ font-size: 2.5em;
517
+ font-weight: bold;
518
+ margin: 0;
519
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
520
+ }
521
+ .subtitle-text {
522
+ color: rgba(255,255,255,0.9);
523
+ font-size: 1.2em;
524
+ margin: 0.5rem 0 0 0;
525
+ }
526
+ .status-box {
527
+ padding: 1rem;
528
+ border-radius: 10px;
529
+ margin: 1rem 0;
530
+ border-left: 4px solid;
531
+ }
532
+ .status-success {
533
+ background-color: #d4edda;
534
+ border-color: #28a745;
535
+ color: #155724;
536
+ }
537
+ .status-error {
538
+ background-color: #f8d7da;
539
+ border-color: #dc3545;
540
+ color: #721c24;
541
+ }
542
+ .feature-box {
543
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
544
+ border: 1px solid #e2e8f0;
545
+ border-radius: 15px;
546
+ padding: 1.5rem;
547
+ margin: 1rem 0;
548
+ box-shadow: 0 4px 6px rgba(0,0,0,0.05);
549
+ }
550
+ .chat-container {
551
+ border-radius: 15px;
552
+ border: 1px solid #e2e8f0;
553
+ overflow: hidden;
554
+ }
555
+ .sidebar {
556
+ background: #f8f9fa;
557
+ border-right: 1px solid #e2e8f0;
558
+ min-height: 600px;
559
+ }
560
+ .login-container {
561
+ max-width: 400px;
562
+ margin: 0 auto;
563
+ background: white;
564
+ padding: 2rem;
565
+ border-radius: 15px;
566
+ box-shadow: 0 10px 25px rgba(0,0,0,0.1);
567
+ }
568
+ .main-content {
569
+ padding: 2rem;
570
+ }
571
+ .diagram-container {
572
+ border: 2px solid #e2e8f0;
573
+ border-radius: 10px;
574
+ padding: 1rem;
575
+ background: white;
576
+ margin: 1rem 0;
577
+ }
578
+ """
579
+ ) as app:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
 
581
+ # State management
582
+ user_state = gr.State(None)
583
+ session_state = gr.State(None)
584
+ sessions_state = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
 
586
+ # Header
587
+ gr.HTML("""
588
+ <div class="header-container">
589
+ <h1 class="header-text">⚡ Power Systems Mini-Consultant</h1>
590
+ <p class="subtitle-text">Advanced AI Assistant for Fault Analysis, Protection Systems & Standards</p>
591
+ </div>
592
+ """)
593
+
594
+ # Status display
595
+ status_class = "status-success" if "✅" in initialization_status else "status-error"
596
+ gr.HTML(f"""
597
+ <div class="status-box {status_class}">
598
+ <strong>System Status:</strong> {initialization_status}
599
+ </div>
600
+ """)
601
+
602
+ # Authentication Section
603
+ with gr.Row():
604
+ with gr.Column(visible=True) as login_section:
605
+ gr.HTML("<h2 style='text-align: center; margin-bottom: 2rem;'>🔐 Please Login or Register</h2>")
 
 
606
 
607
+ with gr.Tabs():
608
+ with gr.TabItem("🔑 Login"):
609
+ with gr.Column(elem_classes=["login-container"]):
610
+ login_username = gr.Textbox(label="Username", placeholder="Enter your username")
611
+ login_password = gr.Textbox(label="Password", type="password", placeholder="Enter your password")
612
+ login_btn = gr.Button("Login", variant="primary", size="lg")
613
+ login_status = gr.Textbox(label="Status", interactive=False)
614
+
615
+ with gr.TabItem("📝 Register"):
616
+ with gr.Column(elem_classes=["login-container"]):
617
+ reg_username = gr.Textbox(label="Username", placeholder="Choose a username")
618
+ reg_email = gr.Textbox(label="Email", placeholder="Enter your email")
619
+ reg_full_name = gr.Textbox(label="Full Name", placeholder="Enter your full name")
620
+ reg_password = gr.Textbox(label="Password", type="password", placeholder="Choose a password (min 6 chars)")
621
+ reg_confirm_password = gr.Textbox(label="Confirm Password", type="password", placeholder="Confirm your password")
622
+ register_btn = gr.Button("Create Account", variant="primary", size="lg")
623
+ register_status = gr.Textbox(label="Status", interactive=False)
624
 
625
+ # Main Application (hidden initially)
626
+ with gr.Column(visible=False) as main_section:
627
+ # User welcome
628
+ user_welcome = gr.HTML()
629
+
630
+ with gr.Tabs():
631
+ # Chat Interface Tab
632
+ with gr.TabItem("💬 AI Consultant Chat"):
633
+ with gr.Row():
634
+ # Sidebar for chat history
635
+ with gr.Column(scale=1, elem_classes=["sidebar"]):
636
+ gr.HTML("<h3 style='text-align: center; margin: 1rem 0;'>📜 Chat History</h3>")
637
+ new_chat_btn = gr.Button("➕ New Chat", variant="primary", size="sm")
638
+ chat_sessions_dropdown = gr.Dropdown(
639
+ label="Previous Chats",
640
+ choices=[],
641
+ value=None,
642
+ interactive=True
643
+ )
644
+
645
+ # Main chat area
646
+ with gr.Column(scale=3, elem_classes=["main-content"]):
647
+ gr.HTML("""
648
+ <div class="feature-box">
649
+ <h3>🤖 Intelligent Power Systems Consultant</h3>
650
+ <p>Ask questions about fault analysis, protection systems, standards, calculations, and more!</p>
651
+ </div>
652
+ """)
653
+
654
+ chatbot = gr.Chatbot(
655
+ height=500,
656
+ placeholder="Your Power Systems AI Consultant will appear here...",
657
+ show_label=False,
658
+ elem_classes=["chat-container"]
659
+ )
660
+
661
+ msg = gr.Textbox(
662
+ placeholder="Ask about faults, protection, standards, calculations, or request diagrams...",
663
+ show_label=False,
664
+ container=False,
665
+ scale=4
666
+ )
667
+
668
+ with gr.Row():
669
+ submit_btn = gr.Button("Send 📤", variant="primary", scale=1)
670
+ clear_btn = gr.Button("Clear Chat 🗑️", variant="secondary", scale=1)
671
+
672
+ # Diagram display
673
+ with gr.Column(visible=True):
674
+ gr.HTML("<h4>📊 Generated Engineering Diagrams</h4>")
675
+ diagram_display = gr.HTML(
676
+ value="<div class='diagram-container'><p style='text-align: center; color: #666;'>Engineering diagrams will appear here when requested.</p></div>",
677
+ elem_classes=["diagram-container"]
678
+ )
679
+
680
+ # Practice Pack Generator Tab
681
+ with gr.TabItem("📚 Practice Pack Generator"):
682
+ gr.HTML("""
683
+ <div class="feature-box">
684
+ <h3>🔬 AI-Generated Practice Questions</h3>
685
+ <p>Generate customized practice packs for power systems topics with varying difficulty levels.</p>
686
+ </div>
687
+ """)
688
 
689
+ with gr.Row():
690
+ with gr.Column(scale=1):
691
+ topic_input = gr.Dropdown(
692
+ choices=[
693
+ "Fault Analysis", "Protection Systems", "Power Flow",
694
+ "Stability Studies", "Relay Coordination", "Transformers",
695
+ "Transmission Lines", "Load Flow", "Short Circuit Analysis",
696
+ "Harmonics", "Power Quality", "SCADA Systems"
697
+ ],
698
+ label="📋 Select Topic",
699
+ value="Fault Analysis"
700
+ )
701
+
702
+ difficulty_input = gr.Radio(
703
+ choices=["Beginner", "Intermediate", "Advanced"],
704
+ label="🎯 Difficulty Level",
705
+ value="Intermediate"
706
+ )
707
+
708
+ num_questions_input = gr.Slider(
709
+ minimum=5,
710
+ maximum=20,
711
+ step=1,
712
+ value=10,
713
+ label="🔢 Number of Questions"
714
+ )
715
+
716
+ generate_btn = gr.Button("Generate Practice Pack 🎯", variant="primary")
717
+
718
+ with gr.Column(scale=2):
719
+ practice_output = gr.Textbox(
720
+ label="📖 Generated Practice Pack",
721
+ lines=20,
722
+ placeholder="Your practice questions will appear here..."
723
+ )
724
+
725
+ # Standards Explorer Tab
726
+ with gr.TabItem("📋 Standards Explorer"):
727
+ gr.HTML("""
728
+ <div class="feature-box">
729
+ <h3>📚 Power Systems Standards Guide</h3>
730
+ <p>Get detailed explanations of IEEE, IEC, and other international standards.</p>
731
+ </div>
732
+ """)
733
 
734
+ with gr.Row():
735
+ with gr.Column(scale=1):
736
+ standard_input = gr.Dropdown(
737
+ choices=[
738
+ "IEEE C37.2", "IEEE 1547", "IEEE 519", "IEEE C57.12.00",
739
+ "IEC 61850", "IEC 60909", "IEC 61131", "IEC 60255",
740
+ "ANSI C84.1", "IEEE 242", "IEEE 399", "IEEE C37.010"
741
+ ],
742
+ label="📑 Select Standard",
743
+ value="IEEE C37.2"
744
+ )
745
+
746
+ explain_btn = gr.Button("Explain Standard 📖", variant="primary")
747
+
748
+ with gr.Column(scale=2):
749
+ standard_output = gr.Textbox(
750
+ label="📋 Standard Explanation",
751
+ lines=15,
752
+ placeholder="Standard explanation will appear here..."
753
+ )
754
+
755
+ # Resources Tab
756
+ with gr.TabItem("📚 Study Resources"):
757
+ gr.HTML("""
758
+ <div class="feature-box">
759
+ <h3>📖 Power Systems Study Guide</h3>
760
+ <p>Essential formulas, concepts, and quick reference materials.</p>
761
+ </div>
762
+ """)
763
 
764
+ with gr.Accordion("⚡ Fault Analysis Formulas", open=False):
765
+ gr.HTML("""
766
+ <div style='background: #f8f9fa; padding: 1.5rem; border-radius: 8px; margin: 1rem 0;'>
767
+ <h4>Three-Phase Fault Current:</h4>
768
+ <p><code style='background: #e9ecef; padding: 0.25rem 0.5rem; border-radius: 4px;'>I_fault = V_nominal / Z_total</code></p>
769
+
770
+ <h4>Line-to-Ground Fault:</h4>
771
+ <p><code style='background: #e9ecef; padding: 0.25rem 0.5rem; border-radius: 4px;'>I_fault = 3 × V_nominal / (Z1 + Z2 + Z0)</code></p>
772
+
773
+ <h4>Line-to-Line Fault:</h4>
774
+ <p><code style='background: #e9ecef; padding: 0.25rem 0.5rem; border-radius: 4px;'>I_fault = √3 × V_nominal / (Z1 + Z2)</code></p>
775
+ </div>
776
+ """)
 
 
 
 
777
 
778
+ with gr.Accordion("🛡️ Protection Principles", open=False):
779
+ gr.HTML("""
780
+ <div style='background: #f8f9fa; padding: 1.5rem; border-radius: 8px; margin: 1rem 0;'>
781
+ <h4>Overcurrent Protection:</h4>
782
+ <ul style='margin-left: 1.5rem;'>
783
+ <li>Inverse Time Characteristics</li>
784
+ <li>Coordination Time Intervals (0.2-0.5s)</li>
785
+ <li>Pickup Current Settings (1.25-1.5