drbinna commited on
Commit
3da4663
·
verified ·
1 Parent(s): 1443740

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +6 -482
app.py CHANGED
@@ -1,37 +1,3 @@
1
- import gradio as gr
2
- import boto3
3
- import json
4
- import os
5
- from botocore.exceptions import ClientError, NoCredentialsError
6
- import time
7
-
8
- # Configuration - These will be set via Hugging Face Spaces environment variables
9
- KNOWLEDGE_BASE_ID = os.environ.get("KNOWLEDGE_BASE_ID", "PLEASE_SET_IN_SPACES_SETTINGS")
10
- AWS_REGION = os.environ.get("AWS_REGION", "us-east-1")
11
- MODEL_ARN = os.environ.get("MODEL_ARN", "arn:aws:bedrock:us-east-1::foundation-model/openai.gpt-oss-120b-1:0")
12
-
13
- # Check if running in demo mode (no credentials set)
14
- DEMO_MODE = KNOWLEDGE_BASE_ID == "PLEASE_SET_IN_SPACES_SETTINGS"
15
-
16
- # Initialize AWS clients
17
- bedrock_agent = None
18
- initialization_error = None
19
-
20
- if not DEMO_MODE:
21
- try:
22
- bedrock_agent = boto3.client(
23
- 'bedrock-agent-runtime',
24
- region_name=AWS_REGION,
25
- aws_access_key_id=os.environ.get('AWS_ACCESS_KEY_ID'),
26
- aws_secret_access_key=os.environ.get('AWS_SECRET_ACCESS_KEY')
27
- )
28
- print(f"✅ AWS Bedrock client initialized in region: {AWS_REGION}")
29
- except Exception as e:
30
- print(f"❌ Error initializing AWS client: {e}")
31
- initialization_error = str(e)
32
- else:
33
- print("🚧 Running in DEMO MODE - Configure environment variables to enable AWS integration")
34
-
35
  def search_knowledge_base(query, search_type="generate"):
36
  """
37
  Search the ORU IT Knowledge Base using Amazon Bedrock
@@ -59,8 +25,6 @@ To enable full functionality:
59
  - Software installation guides
60
  - Network troubleshooting steps
61
  - Campus system access instructions
62
-
63
- **Cost Savings:** Using S3 Vectors provides up to 90% cost savings compared to traditional vector databases.
64
  """
65
 
66
  demo_sources = """
@@ -82,21 +46,21 @@ To enable full functionality:
82
 
83
  try:
84
  if search_type == "generate":
85
- # Use RetrieveAndGenerate for full responses
86
  response = bedrock_agent.retrieve_and_generate(
87
- input={
88
- 'text': query
89
- },
90
  retrieveAndGenerateConfiguration={
91
  'type': 'KNOWLEDGE_BASE',
92
  'knowledgeBaseConfiguration': {
93
  'knowledgeBaseId': KNOWLEDGE_BASE_ID,
94
- 'modelArn': MODEL_ARN,
95
  'retrievalConfiguration': {
96
  'vectorSearchConfiguration': {
97
  'numberOfResults': 5
98
  }
99
  }
 
 
 
100
  }
101
  }
102
  )
@@ -123,9 +87,7 @@ To enable full functionality:
123
  # Use Retrieve for sources only
124
  response = bedrock_agent.retrieve(
125
  knowledgeBaseId=KNOWLEDGE_BASE_ID,
126
- retrievalQuery={
127
- 'text': query
128
- },
129
  retrievalConfiguration={
130
  'vectorSearchConfiguration': {
131
  'numberOfResults': 5
@@ -133,9 +95,7 @@ To enable full functionality:
133
  }
134
  )
135
 
136
- # Extract retrieved results
137
  retrieval_results = response.get('retrievalResults', [])
138
-
139
  if not retrieval_results:
140
  return "No relevant sources found.", "", "warning"
141
 
@@ -144,7 +104,6 @@ To enable full functionality:
144
  content = result.get('content', {}).get('text', 'No content available')
145
  score = result.get('score', 0)
146
  location = result.get('location', {}).get('s3Location', {}).get('uri', 'Unknown source')
147
-
148
  sources_info.append(
149
  f"**Source {i} (Confidence: {score:.2f}):**\n"
150
  f"{content[:400]}{'...' if len(content) > 400 else ''}\n"
@@ -166,438 +125,3 @@ To enable full functionality:
166
 
167
  except Exception as e:
168
  return f"❌ Unexpected error: {str(e)}", "", "error"
169
-
170
- def format_response(query, search_type):
171
- """Process the query and return formatted response"""
172
-
173
- if not query.strip():
174
- return (
175
- "Please enter your IT question above and click 'Get AI Answer' or 'Show Sources'.",
176
- "",
177
- "Enter a question to get started!"
178
- )
179
-
180
- # Perform the search
181
- response_text, sources_text, status = search_knowledge_base(query, search_type)
182
-
183
- # Format the final response based on search type and status
184
- if status == "demo":
185
- formatted_response = response_text
186
- status_msg = "🚧 Demo Mode - Configure AWS credentials to enable full functionality"
187
- elif search_type == "generate":
188
- if status == "success":
189
- formatted_response = f"## 🤖 AI Assistant Response\n\n{response_text}"
190
- status_msg = "✅ Response generated successfully from ORU IT Knowledge Base!"
191
- else:
192
- formatted_response = response_text
193
- status_msg = "❌ Error generating response"
194
- else:
195
- if status == "success":
196
- formatted_response = f"## 📚 Retrieved Sources\n\n{response_text}"
197
- status_msg = "✅ Sources retrieved successfully from ORU IT Knowledge Base!"
198
- else:
199
- formatted_response = response_text
200
- status_msg = "❌ Error retrieving sources"
201
-
202
- return formatted_response, sources_text, status_msg
203
-
204
- # Sample queries for ORU IT scenarios
205
- SAMPLE_QUERIES = [
206
- "How do I change my student password?",
207
- "My ORU app isn't showing the right tiles",
208
- "Word is showing view only mode on my Mac",
209
- "How do I set up multi-factor authentication?",
210
- "I can't edit Microsoft Word documents",
211
- "How do I connect to ORU WiFi network?",
212
- "My ORU email isn't syncing to my phone",
213
- "How do I access Vision portal from off-campus?",
214
- "VPN connection keeps dropping",
215
- "Can't print to campus printers from my laptop",
216
- "How do I reset my Z-number password?",
217
- "Microsoft Teams isn't working properly"
218
- ]
219
-
220
- # Custom CSS with ORU Brand Colors
221
- custom_css = """
222
- :root {
223
- --oru-primary-blue: #041E41;
224
- --oru-primary-tan: #C4B282;
225
- --oru-secondary-tan: #E9DFBB;
226
- --oru-accent-blue: #7BAFD4;
227
- --oru-accent-tan: #C39236;
228
- }
229
-
230
- .gradio-container {
231
- max-width: 1200px !important;
232
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
233
- }
234
-
235
- /* Header styling */
236
- .oru-header {
237
- background: linear-gradient(135deg, var(--oru-primary-blue) 0%, var(--oru-accent-blue) 100%);
238
- color: white;
239
- padding: 30px;
240
- text-align: center;
241
- border-radius: 15px;
242
- margin-bottom: 25px;
243
- box-shadow: 0 8px 25px rgba(4, 30, 65, 0.2);
244
- }
245
-
246
- .oru-header h1 {
247
- font-size: 2.5rem;
248
- margin-bottom: 10px;
249
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
250
- }
251
-
252
- .oru-header p {
253
- font-size: 1.1rem;
254
- opacity: 0.95;
255
- margin: 8px 0;
256
- }
257
-
258
- /* Button styling */
259
- .gradio-button {
260
- border-radius: 8px !important;
261
- font-weight: 600 !important;
262
- transition: all 0.3s ease !important;
263
- border: none !important;
264
- }
265
-
266
- .gradio-button.primary {
267
- background: linear-gradient(45deg, var(--oru-primary-blue), var(--oru-accent-blue)) !important;
268
- color: white !important;
269
- }
270
-
271
- .gradio-button.primary:hover {
272
- transform: translateY(-2px) !important;
273
- box-shadow: 0 6px 20px rgba(4, 30, 65, 0.3) !important;
274
- }
275
-
276
- .gradio-button.secondary {
277
- background: linear-gradient(45deg, var(--oru-accent-tan), var(--oru-primary-tan)) !important;
278
- color: white !important;
279
- }
280
-
281
- .gradio-button.secondary:hover {
282
- transform: translateY(-2px) !important;
283
- box-shadow: 0 6px 20px rgba(195, 146, 54, 0.3) !important;
284
- }
285
-
286
- /* Small sample buttons */
287
- .gradio-button[size="sm"] {
288
- background: var(--oru-secondary-tan) !important;
289
- color: var(--oru-primary-blue) !important;
290
- border: 1px solid var(--oru-primary-tan) !important;
291
- font-size: 14px !important;
292
- padding: 8px 12px !important;
293
- }
294
-
295
- .gradio-button[size="sm"]:hover {
296
- background: var(--oru-primary-tan) !important;
297
- color: white !important;
298
- transform: translateY(-1px) !important;
299
- }
300
-
301
- /* Input styling */
302
- .gradio-textbox textarea, .gradio-textbox input {
303
- border: 2px solid var(--oru-secondary-tan) !important;
304
- border-radius: 8px !important;
305
- font-size: 16px !important;
306
- }
307
-
308
- .gradio-textbox textarea:focus, .gradio-textbox input:focus {
309
- border-color: var(--oru-accent-blue) !important;
310
- box-shadow: 0 0 0 3px rgba(123, 175, 212, 0.2) !important;
311
- }
312
-
313
- /* Output styling */
314
- .output-markdown h2 {
315
- color: var(--oru-primary-blue) !important;
316
- border-bottom: 3px solid var(--oru-accent-blue) !important;
317
- padding-bottom: 10px !important;
318
- margin-top: 20px !important;
319
- }
320
-
321
- .output-markdown h3 {
322
- color: var(--oru-accent-tan) !important;
323
- }
324
-
325
- .output-markdown {
326
- line-height: 1.6 !important;
327
- }
328
-
329
- /* Status messages */
330
- .status-success {
331
- background: linear-gradient(45deg, #d4edda, var(--oru-secondary-tan)) !important;
332
- border: 2px solid var(--oru-primary-tan) !important;
333
- color: var(--oru-primary-blue) !important;
334
- padding: 15px !important;
335
- border-radius: 10px !important;
336
- font-weight: 600 !important;
337
- }
338
-
339
- .status-error {
340
- background: linear-gradient(45deg, #f8d7da, #fde2e4) !important;
341
- border: 2px solid #dc3545 !important;
342
- color: #721c24 !important;
343
- padding: 15px !important;
344
- border-radius: 10px !important;
345
- font-weight: 600 !important;
346
- }
347
-
348
- /* Demo mode styling */
349
- .status-demo {
350
- background: linear-gradient(45deg, #fff3cd, var(--oru-secondary-tan)) !important;
351
- border: 2px solid var(--oru-accent-tan) !important;
352
- color: var(--oru-primary-blue) !important;
353
- padding: 15px !important;
354
- border-radius: 10px !important;
355
- font-weight: 600 !important;
356
- }
357
-
358
- /* Configuration panel */
359
- .config-panel {
360
- background: linear-gradient(45deg, var(--oru-secondary-tan), #f8f9fa) !important;
361
- padding: 20px !important;
362
- border-radius: 10px !important;
363
- border: 1px solid var(--oru-primary-tan) !important;
364
- }
365
-
366
- /* Accordion styling */
367
- .gradio-accordion {
368
- border: 1px solid var(--oru-primary-tan) !important;
369
- border-radius: 10px !important;
370
- }
371
-
372
- .gradio-accordion summary {
373
- background: var(--oru-secondary-tan) !important;
374
- color: var(--oru-primary-blue) !important;
375
- font-weight: 600 !important;
376
- padding: 15px !important;
377
- }
378
-
379
- /* Sample queries section */
380
- .sample-queries {
381
- background: linear-gradient(45deg, var(--oru-secondary-tan), #ffffff) !important;
382
- padding: 20px !important;
383
- border-radius: 10px !important;
384
- border: 1px solid var(--oru-primary-tan) !important;
385
- margin-top: 15px !important;
386
- }
387
-
388
- .sample-queries h3 {
389
- color: var(--oru-primary-blue) !important;
390
- margin-bottom: 15px !important;
391
- font-size: 1.2rem !important;
392
- }
393
-
394
- /* Source citations styling */
395
- .output-markdown strong {
396
- color: var(--oru-accent-tan) !important;
397
- }
398
-
399
- .output-markdown em {
400
- color: var(--oru-primary-blue) !important;
401
- font-style: italic !important;
402
- }
403
- """
404
-
405
- # Create the Gradio interface
406
- with gr.Blocks(css=custom_css, title="ORU IT Helpdesk Assistant") as app:
407
-
408
- # Header with ORU Branding
409
- gr.HTML("""
410
- <div class="oru-header">
411
- <h1>🏛️ Oral Roberts University</h1>
412
- <h2 style="font-size: 2rem; margin: 15px 0; font-weight: 600;">IT Helpdesk Assistant</h2>
413
- <p style="font-size: 18px; margin: 10px 0;">Powered by Amazon Bedrock Knowledge Bases & S3 Vectors</p>
414
- <p style="font-size: 16px; opacity: 0.95; margin: 5px 0;">Get instant answers to your IT questions • Available 24/7</p>
415
- <div style="margin-top: 20px; padding: 10px; background: rgba(255,255,255,0.1); border-radius: 8px; display: inline-block;">
416
- <span style="font-size: 14px; opacity: 0.9;">🔍 Search • 📚 Retrieve Sources • ⚡ Instant Responses</span>
417
- </div>
418
- </div>
419
- """)
420
-
421
- with gr.Row():
422
- with gr.Column(scale=2):
423
- # Input section
424
- query_input = gr.Textbox(
425
- label="🔍 Ask your IT question:",
426
- placeholder="e.g., How do I change my password?",
427
- lines=2,
428
- max_lines=4
429
- )
430
-
431
- with gr.Row():
432
- search_btn = gr.Button(
433
- "🤖 Get AI Answer",
434
- variant="primary",
435
- scale=2,
436
- elem_classes=["primary"]
437
- )
438
- retrieve_btn = gr.Button(
439
- "📚 Show Sources",
440
- variant="secondary",
441
- scale=1,
442
- elem_classes=["secondary"]
443
- )
444
-
445
- # Sample queries with ORU styling
446
- gr.HTML("""
447
- <div class="sample-queries">
448
- <h3>💡 Try These Common ORU IT Questions:</h3>
449
- <p style="color: #041E41; margin-bottom: 15px; font-size: 14px;">
450
- Click any question below to auto-fill the search box ⬆️
451
- </p>
452
- </div>
453
- """)
454
-
455
- with gr.Column():
456
- for i in range(0, len(SAMPLE_QUERIES), 2):
457
- with gr.Row():
458
- if i < len(SAMPLE_QUERIES):
459
- sample_btn1 = gr.Button(
460
- SAMPLE_QUERIES[i],
461
- size="sm",
462
- elem_classes=["sample-query-btn"]
463
- )
464
- sample_btn1.click(lambda x=SAMPLE_QUERIES[i]: x, outputs=query_input)
465
- if i + 1 < len(SAMPLE_QUERIES):
466
- sample_btn2 = gr.Button(
467
- SAMPLE_QUERIES[i + 1],
468
- size="sm",
469
- elem_classes=["sample-query-btn"]
470
- )
471
- sample_btn2.click(lambda x=SAMPLE_QUERIES[i + 1]: x, outputs=query_input)
472
-
473
- with gr.Column(scale=3):
474
- # Output section
475
- status_output = gr.HTML(label="Status")
476
- response_output = gr.Markdown(label="Response")
477
- sources_output = gr.Markdown(label="Sources", visible=True)
478
-
479
- # Configuration info with ORU branding
480
- with gr.Accordion("🔧 System Configuration", open=False):
481
- gr.HTML(f"""
482
- <div class="config-panel">
483
- <h3 style="color: #041E41; margin-bottom: 15px; font-size: 1.3rem;">📊 Knowledge Base Configuration</h3>
484
- <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 15px;">
485
- <div>
486
- <p><strong style="color: #C39236;">Knowledge Base ID:</strong><br>
487
- <code style="background: #E9DFBB; padding: 5px; border-radius: 3px; color: #041E41;">{KNOWLEDGE_BASE_ID}</code></p>
488
- <p><strong style="color: #C39236;">AWS Region:</strong><br>
489
- <code style="background: #E9DFBB; padding: 5px; border-radius: 3px; color: #041E41;">{AWS_REGION}</code></p>
490
- </div>
491
- <div>
492
- <p><strong style="color: #C39236;">AI Model:</strong><br>
493
- <span style="color: #041E41;">OpenAI GPT-OSS-120B</span></p>
494
- <p><strong style="color: #C39236;">Connection Status:</strong><br>
495
- <span style="color: {'#28a745' if bedrock_agent else ('#f39c12' if DEMO_MODE else '#dc3545')}; font-weight: 600;">
496
- {'✅ Connected & Ready' if bedrock_agent else ('🚧 Demo Mode - Configure AWS' if DEMO_MODE else '❌ Connection Error')}
497
- </span></p>
498
- </div>
499
- </div>
500
- <div style="margin-top: 15px; padding: 10px; background: rgba(123, 175, 212, 0.1); border-radius: 5px; border-left: 4px solid #7BAFD4;">
501
- <small style="color: #041E41;"><strong>💡 Tip:</strong> This system uses ORU's IT knowledge base to provide accurate, up-to-date answers to common technical questions.</small>
502
- </div>
503
- </div>
504
- """)
505
-
506
- # Setup instructions for Hugging Face Spaces
507
- with gr.Accordion("📋 Hugging Face Spaces Configuration", open=False):
508
- gr.Markdown(f"""
509
- ## 🚀 Configure This Space for ORU IT:
510
-
511
- ### Current Status:
512
- - **Demo Mode:** {'🚧 Active' if DEMO_MODE else '✅ Disabled - AWS configured'}
513
- - **Knowledge Base ID:** `{KNOWLEDGE_BASE_ID}`
514
- - **AWS Region:** `{AWS_REGION}`
515
-
516
- ### ⚙️ Required Environment Variables:
517
- To enable full functionality, add these in your **Space Settings > Variables and secrets**:
518
-
519
- ```bash
520
- KNOWLEDGE_BASE_ID=YOUR_ACTUAL_KNOWLEDGE_BASE_ID
521
- AWS_REGION=us-east-1
522
- AWS_ACCESS_KEY_ID=your_aws_access_key
523
- AWS_SECRET_ACCESS_KEY=your_aws_secret_key
524
- ```
525
-
526
- ### 📝 Setup Steps:
527
-
528
- 1. **Get Your Knowledge Base ID:**
529
- - Go to AWS Bedrock Console
530
- - Navigate to Knowledge Bases
531
- - Find your "ORU IT Helpdesk Knowledge Base"
532
- - Copy the Knowledge Base ID
533
-
534
- 2. **Configure AWS Credentials:**
535
- - Use an IAM user with Bedrock permissions
536
- - Required permissions: `bedrock:InvokeModel`, `bedrock:Retrieve`, `bedrock:RetrieveAndGenerate`
537
-
538
- 3. **Add to Hugging Face Spaces:**
539
- - Go to your Space settings
540
- - Click "Variables and secrets"
541
- - Add each environment variable as a "Secret"
542
- - The Space will automatically restart with new configuration
543
-
544
- 4. **Test the Integration:**
545
- - Once configured, the "🚧 Demo Mode" status will change to "✅ Connected"
546
- - Test with sample queries to verify everything works
547
-
548
- ### 🔒 Security Best Practices:
549
- - ✅ Use IAM user with minimal required permissions
550
- - ✅ Store credentials as Secrets (not Variables) in Spaces
551
- - ✅ Monitor AWS CloudTrail for API usage
552
- - ✅ Regularly rotate access keys
553
-
554
- ### 💰 Cost Optimization:
555
- - S3 Vectors provides up to 90% cost savings vs traditional vector databases
556
- - Monitor your AWS Bedrock usage in the AWS console
557
- - Consider setting up billing alerts
558
- """)
559
-
560
- # Event handlers
561
- search_btn.click(
562
- fn=lambda query: format_response(query, "generate"),
563
- inputs=[query_input],
564
- outputs=[response_output, sources_output, status_output]
565
- )
566
-
567
- retrieve_btn.click(
568
- fn=lambda query: format_response(query, "retrieve"),
569
- inputs=[query_input],
570
- outputs=[response_output, sources_output, status_output]
571
- )
572
-
573
- # Allow Enter key to trigger search
574
- query_input.submit(
575
- fn=lambda query: format_response(query, "generate"),
576
- inputs=[query_input],
577
- outputs=[response_output, sources_output, status_output]
578
- )
579
-
580
- # Launch configuration
581
- if __name__ == "__main__":
582
- print("🏛️ Starting ORU IT Helpdesk Assistant...")
583
- print("🎓 Oral Roberts University - Information Technology")
584
- print(f"📚 Knowledge Base ID: {KNOWLEDGE_BASE_ID}")
585
- print(f"🌍 AWS Region: {AWS_REGION}")
586
- print(f"🚧 Demo Mode: {'Active' if DEMO_MODE else 'Disabled'}")
587
- print("=" * 50)
588
- if DEMO_MODE:
589
- print("🔧 Configuration needed:")
590
- print(" □ Set KNOWLEDGE_BASE_ID environment variable")
591
- print(" □ Set AWS_ACCESS_KEY_ID environment variable")
592
- print(" □ Set AWS_SECRET_ACCESS_KEY environment variable")
593
- print(" □ Verify Bedrock model access is enabled")
594
- else:
595
- print("✅ Configuration looks good!")
596
- print("🔧 Checklist:")
597
- print(" ✅ Environment variables configured")
598
- print(" □ Test with sample queries")
599
- print(" □ Verify knowledge base content")
600
- print("=" * 50)
601
- print("🚀 Ready to serve ORU students, faculty, and staff!")
602
-
603
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  def search_knowledge_base(query, search_type="generate"):
2
  """
3
  Search the ORU IT Knowledge Base using Amazon Bedrock
 
25
  - Software installation guides
26
  - Network troubleshooting steps
27
  - Campus system access instructions
 
 
28
  """
29
 
30
  demo_sources = """
 
46
 
47
  try:
48
  if search_type == "generate":
49
+ # Corrected RetrieveAndGenerate with separate modelConfiguration
50
  response = bedrock_agent.retrieve_and_generate(
51
+ input={'text': query},
 
 
52
  retrieveAndGenerateConfiguration={
53
  'type': 'KNOWLEDGE_BASE',
54
  'knowledgeBaseConfiguration': {
55
  'knowledgeBaseId': KNOWLEDGE_BASE_ID,
 
56
  'retrievalConfiguration': {
57
  'vectorSearchConfiguration': {
58
  'numberOfResults': 5
59
  }
60
  }
61
+ },
62
+ 'modelConfiguration': {
63
+ 'modelArn': MODEL_ARN
64
  }
65
  }
66
  )
 
87
  # Use Retrieve for sources only
88
  response = bedrock_agent.retrieve(
89
  knowledgeBaseId=KNOWLEDGE_BASE_ID,
90
+ retrievalQuery={'text': query},
 
 
91
  retrievalConfiguration={
92
  'vectorSearchConfiguration': {
93
  'numberOfResults': 5
 
95
  }
96
  )
97
 
 
98
  retrieval_results = response.get('retrievalResults', [])
 
99
  if not retrieval_results:
100
  return "No relevant sources found.", "", "warning"
101
 
 
104
  content = result.get('content', {}).get('text', 'No content available')
105
  score = result.get('score', 0)
106
  location = result.get('location', {}).get('s3Location', {}).get('uri', 'Unknown source')
 
107
  sources_info.append(
108
  f"**Source {i} (Confidence: {score:.2f}):**\n"
109
  f"{content[:400]}{'...' if len(content) > 400 else ''}\n"
 
125
 
126
  except Exception as e:
127
  return f"❌ Unexpected error: {str(e)}", "", "error"