kgauvin603 commited on
Commit
7da4837
Β·
verified Β·
1 Parent(s): 8d322d4

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +357 -0
app.py ADDED
@@ -0,0 +1,357 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # === Imports ===
2
+ import os
3
+ import oci
4
+ import re
5
+ import gradio as gr
6
+ import openai
7
+ import oci
8
+ from datetime import datetime
9
+ from bs4 import BeautifulSoup
10
+ from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer
11
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
12
+ from reportlab.lib.pagesizes import letter
13
+ from reportlab.lib.enums import TA_CENTER
14
+ from reportlab.lib import colors
15
+ import tempfile
16
+
17
+
18
+ # --- API Keys ---
19
+ openai_api_key = os.environ.get("OPENAI_API_KEY")
20
+ if not openai_api_key:
21
+ raise ValueError("OPENAI_API_KEY environment variable is not set.")
22
+
23
+ client = openai.OpenAI(api_key=openai_api_key)
24
+
25
+ openrouter_key = os.environ.get("OPENROUTER")
26
+ openrouter = openai.OpenAI(api_key=openrouter_key, base_url="https://openrouter.ai/api/v1")
27
+
28
+ # --- OCI Object Storage: Explicit Fixed Configuration ---
29
+
30
+ # === OCI Object Storage Setup ===
31
+ oci_config = {
32
+ "user": os.environ.get("OCI_USER"),
33
+ "tenancy": os.environ.get("OCI_TENANCY"),
34
+ "fingerprint": os.environ.get("OCI_FINGERPRINT"),
35
+ "region": os.environ.get("OCI_REGION"),
36
+ "key_content": os.environ.get("OCI_PRIVATE_KEY")
37
+ }
38
+
39
+ namespace = os.environ.get("OCI_NAMESPACE")
40
+ bucket_name = os.environ.get("OCI_BUCKET_NAME")
41
+
42
+ try:
43
+ object_storage = oci.object_storage.ObjectStorageClient(oci_config)
44
+ except Exception as e:
45
+ print("Failed to initialize OCI Object Storage client:", e)
46
+
47
+ """
48
+ # --- OCI Object Storage Setup ---
49
+ oci_config = {
50
+ "user": os.environ.get("OCI_USER"),
51
+ "tenancy": os.environ.get("OCI_TENANCY"),
52
+ "fingerprint": os.environ.get("OCI_FINGERPRINT"),
53
+ "region": os.environ.get("OCI_REGION"),
54
+ "key_content": os.environ.get("OCI_PRIVATE_KEY")
55
+ }
56
+ namespace = os.environ.get("OCI_NAMESPACE")
57
+ bucket_name = os.environ.get("OCI_BUCKET_NAME")
58
+ os.environ["OCI_BUCKET_NAME"] = "OracleTANGO"
59
+ try:
60
+ object_storage = oci.object_storage.ObjectStorageClient(oci_config)
61
+ except Exception as e:
62
+ print("Failed to initialize OCI Object Storage client:", e)
63
+ """
64
+ # --- Exadata Specs ---
65
+ exadata_specs = {
66
+ "X7": {"Quarter Rack": {"max_iops": 350000, "max_throughput": 25}, "Half Rack": {"max_iops": 700000, "max_throughput": 50}, "Full Rack": {"max_iops": 1400000, "max_throughput": 100}},
67
+ "X8": {"Quarter Rack": {"max_iops": 380000, "max_throughput": 28}, "Half Rack": {"max_iops": 760000, "max_throughput": 56}, "Full Rack": {"max_iops": 1520000, "max_throughput": 112}},
68
+ "X9": {"Quarter Rack": {"max_iops": 450000, "max_throughput": 30}, "Half Rack": {"max_iops": 900000, "max_throughput": 60}, "Full Rack": {"max_iops": 1800000, "max_throughput": 120}},
69
+ "X10": {"Quarter Rack": {"max_iops": 500000, "max_throughput": 35}, "Half Rack": {"max_iops": 1000000, "max_throughput": 70}, "Full Rack": {"max_iops": 2000000, "max_throughput": 140}},
70
+ "X11M": {"Quarter Rack": {"max_iops": 600000, "max_throughput": 40}, "Half Rack": {"max_iops": 1200000, "max_throughput": 80}, "Full Rack": {"max_iops": 2400000, "max_throughput": 160}},
71
+ }
72
+
73
+ # --- Supported LLM Models ---
74
+ supported_llms = {
75
+ "gpt-3.5-turbo": "Fastest / Lowest Cost - General AWR Healthcheck",
76
+ "gpt-4-turbo": "Balanced - Production Performance Analysis",
77
+ "gpt-4o": "Deepest Analysis - Exadata, RAC, Smart Scan, Critical Issues",
78
+ "gpt-4.1": "Great for quick coding and analysis",
79
+ }
80
+
81
+ # --- Utils ---
82
+ def clean_awr_content(content):
83
+ if "<html" in content.lower():
84
+ soup = BeautifulSoup(content, "html.parser")
85
+ return soup.get_text()
86
+ return content
87
+
88
+ def upload_awr_file(file_obj):
89
+ filename = os.path.basename(file_obj)
90
+ with open(file_obj, "rb") as f:
91
+ content = f.read()
92
+ object_storage.put_object(namespace, bucket_name, filename, content)
93
+ return f"\u2705 Uploaded {filename}"
94
+
95
+ def list_awr_files():
96
+ try:
97
+ objects = object_storage.list_objects(namespace, bucket_name)
98
+ return [obj.name for obj in objects.data.objects if obj.name.endswith(".html") or obj.name.endswith(".txt")]
99
+ except Exception as e:
100
+ return [f"Error listing objects: {str(e)}"]
101
+
102
+ def get_awr_file_text(filename):
103
+ try:
104
+ response = object_storage.get_object(namespace, bucket_name, filename)
105
+ raw = response.data.content.decode()
106
+ return clean_awr_content(raw)
107
+ except Exception as e:
108
+ return f"Error loading file: {str(e)}"
109
+
110
+ def generate_pdf(analysis_text, health_text, rating_text, retry_status_text):
111
+ # Temporary file path
112
+ temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".pdf")
113
+ pdf_path = temp_file.name
114
+
115
+ # PDF setup
116
+ doc = SimpleDocTemplate(pdf_path, pagesize=letter)
117
+ styles = getSampleStyleSheet()
118
+ elements = []
119
+
120
+ # Custom header style
121
+ header_style = ParagraphStyle(
122
+ name="HeaderStyle",
123
+ fontSize=16,
124
+ alignment=TA_CENTER,
125
+ textColor=colors.darkblue,
126
+ spaceAfter=14
127
+ )
128
+
129
+ # Section title style
130
+ section_style = ParagraphStyle(
131
+ name="SectionHeader",
132
+ fontSize=14,
133
+ textColor=colors.darkred,
134
+ spaceAfter=8
135
+ )
136
+
137
+ # Body text style
138
+ body_style = ParagraphStyle(
139
+ name="BodyStyle",
140
+ fontSize=10,
141
+ leading=14,
142
+ spaceAfter=10
143
+ )
144
+
145
+ # Title
146
+ elements.append(Paragraph("Oracle AWR Analyzer Report", header_style))
147
+ elements.append(Spacer(1, 12))
148
+
149
+ # Sections
150
+ sections = [
151
+ ("AWR Analysis", analysis_text),
152
+ ("Health Agent Findings", health_text),
153
+ ("Rater Output", rating_text),
154
+ ("Retry Status", retry_status_text)
155
+ ]
156
+
157
+ for title, content in sections:
158
+ elements.append(Paragraph(title, section_style))
159
+ elements.append(Paragraph(content.replace("\n", "<br/>"), body_style))
160
+ elements.append(Spacer(1, 12))
161
+
162
+ # Build PDF
163
+ doc.build(elements)
164
+
165
+ return pdf_path
166
+
167
+ def compare_awrs(file_list, llm_model):
168
+ if not file_list:
169
+ return "No files selected."
170
+ combined_text = ""
171
+ for fname in file_list:
172
+ content = get_awr_file_text(fname)
173
+ combined_text += f"\n=== AWR: {fname} ===\n{content[:3000]}...\n"
174
+ prompt = f"""You are a senior Oracle performance engineer. You will compare multiple AWR reports and highlight:
175
+ - Key differences in workload or system behavior
176
+ - Major trends or anomalies
177
+ - Which report shows better performance and why
178
+ - Exadata-specific metrics like Smart Scan, Flash I/O
179
+ - Suggestions to unify or improve system behavior
180
+ AWR Reports:
181
+ {combined_text}
182
+ """
183
+ response = client.chat.completions.create(
184
+ model=llm_model,
185
+ messages=[
186
+ {"role": "system", "content": "You are a comparative AWR analysis expert."},
187
+ {"role": "user", "content": prompt}
188
+ ]
189
+ )
190
+ return response.choices[0].message.content.strip()
191
+
192
+ # === AGENTS ===
193
+ class CriticalAnalyzerAgent:
194
+ def analyze(self, content, performance_test_mode, exadata_model, rack_size, llm_model):
195
+ cleaned_content = clean_awr_content(content)
196
+ if len(cleaned_content) > 128000:
197
+ cleaned_content = cleaned_content[:128000] + "\n\n[TRUNCATED]..."
198
+
199
+ prompt = f"""You are an expert Oracle DBA performance analyst specialized in AWR + Exadata.
200
+ Please perform advanced analysis on the following report:
201
+ ======== AWR REPORT START ========
202
+ {cleaned_content}
203
+ ======== AWR REPORT END ========
204
+ Required Output:
205
+ - Performance Summary (with metric values)
206
+ - Detailed Bottlenecks + Risks (quantified)
207
+ - Forecast + Predictions
208
+ - Monitoring Recommendations
209
+ - Exadata Statistics (IO, Flash Cache, Smart Scan)
210
+ - Recommended Next Steps to Bridge Gaps
211
+ """
212
+ if performance_test_mode and exadata_model and rack_size:
213
+ specs = exadata_specs.get(exadata_model, {}).get(rack_size, {})
214
+ if specs:
215
+ prompt += f"""
216
+ This was a PERFORMANCE TEST on Oracle Exadata {exadata_model} {rack_size}.
217
+ Theoretical Max:
218
+ - IOPS: {specs['max_iops']}
219
+ - Throughput: {specs['max_throughput']} GB/s
220
+ Compare observed vs theoretical. Recommend actions to close the performance gap.
221
+ """
222
+ response = client.chat.completions.create(
223
+ model=llm_model,
224
+ messages=[
225
+ {"role": "system", "content": "You are an expert Oracle DBA."},
226
+ {"role": "user", "content": prompt}
227
+ ]
228
+ )
229
+ return response.choices[0].message.content.strip()
230
+
231
+ class HealthAgent:
232
+ def check_health(self, content, llm_model):
233
+ cleaned_content = clean_awr_content(content)
234
+ if len(cleaned_content) > 128000:
235
+ cleaned_content = cleaned_content[:128000] + "\n\n[TRUNCATED]..."
236
+
237
+ prompt = f"""You are the Oracle AWR Health Analysis Agent.
238
+ Your primary responsibility is to detect and report ANY and ALL database health risks, alerts, warnings, or failures in the AWR report.
239
+ You MUST:
240
+ - Identify all issues marked as CRITICAL, WARNING, ALERT, FAILED, OFFLINE, CONFINED, DROPPED, or ERROR.
241
+ - Never omit or generalize. If something appears important, call it out.
242
+ - Classify each issue into: 🚨 CRITICAL / ⚠️ WARNING / βœ… INFO
243
+ - For CRITICAL and WARNING, provide suggested actions or considerations.
244
+ - Always confirm at the end if no CRITICAL or WARNING issues were found.
245
+ Special Attention Areas:
246
+ - Flash Cache or Flash Disk Failures
247
+ - I/O Subsystem stalls or errors
248
+ - ASM/Grid Disk issues
249
+ - Smart Scan failures
250
+ - Redo Log issues
251
+ - RAC Interconnect issues
252
+ AWR CONTENT:
253
+ {cleaned_content}
254
+ """
255
+ response = client.chat.completions.create(
256
+ model=llm_model,
257
+ messages=[
258
+ {"role": "system", "content": "You are the strict Oracle AWR Health Analysis Agent."},
259
+ {"role": "user", "content": prompt}
260
+ ]
261
+ )
262
+ return response.choices[0].message.content.strip()
263
+
264
+ class RaterAgent:
265
+ def rate(self, content):
266
+ prompt = f"Rate the following analysis from 1-5 stars and explain:\n\n{content}"
267
+ response = openrouter.chat.completions.create(
268
+ model="mistralai/Mixtral-8x7B-Instruct",
269
+ messages=[{"role": "user", "content": prompt}]
270
+ )
271
+ return response.choices[0].message.content.strip()
272
+
273
+ # === MAIN AWR PROCESS ===
274
+ def process_awr(awr_text, threshold, performance_test_mode, exadata_model, rack_size, llm_model):
275
+ analyzer = CriticalAnalyzerAgent()
276
+ health = HealthAgent()
277
+ rater = RaterAgent()
278
+
279
+ if not awr_text.strip():
280
+ return "No AWR text provided", "", "", ""
281
+
282
+ analysis = analyzer.analyze(awr_text, performance_test_mode, exadata_model, rack_size, llm_model)
283
+ health_status = health.check_health(awr_text, llm_model)
284
+ rating_text = rater.rate(analysis)
285
+
286
+ stars = 0
287
+ match = re.search(r"(\d+)", rating_text)
288
+ if match:
289
+ stars = int(match.group(1))
290
+
291
+ retry_status = "βœ… Accepted"
292
+ if stars < threshold:
293
+ analysis = analyzer.analyze(awr_text, performance_test_mode, exadata_model, rack_size, llm_model)
294
+ rating_text = rater.rate(analysis)
295
+ retry_status = "βœ… Retry Occurred"
296
+
297
+ return analysis, health_status, rating_text, retry_status
298
+
299
+ # === Gradio UI ===
300
+ with gr.Tab("Manual AWR Analysis"):
301
+ gr.Markdown("# Multi-Agent Oracle AWR Analyzer (Production Edition)")
302
+
303
+ awr_file = gr.File(label="Upload AWR Report (.html or .txt)", file_types=[".html", ".txt"])
304
+ awr_text = gr.Textbox(label="AWR Report (pasted or loaded)", lines=30)
305
+ awr_file.upload(awr_file_to_text, inputs=awr_file, outputs=awr_text)
306
+
307
+ threshold = gr.Slider(0, 5, value=3, step=1, label="Correctness Threshold (Stars)")
308
+ performance_test_mode = gr.Checkbox(label="Performance Test Mode")
309
+ exadata_model = gr.Dropdown(choices=list(exadata_specs.keys()), label="Exadata Model", visible=False)
310
+ rack_size = gr.Dropdown(choices=["Quarter Rack", "Half Rack", "Full Rack"], label="Rack Size", visible=False)
311
+ llm_selector = gr.Dropdown(choices=list(supported_llms.keys()), value="gpt-4-turbo", label="LLM Model")
312
+
313
+ performance_test_mode.change(toggle_visibility, inputs=performance_test_mode, outputs=[exadata_model, rack_size])
314
+ analyze_btn = gr.Button("Analyze AWR Report")
315
+ output = gr.Textbox(label="AWR Analysis", lines=20)
316
+ health = gr.Textbox(label="Health Agent Findings", lines=10)
317
+ rating = gr.Textbox(label="Rater", lines=3)
318
+ retry_status = gr.Textbox(label="Retry Status")
319
+
320
+ analyze_btn.click(
321
+ process_awr,
322
+ inputs=[awr_text, threshold, performance_test_mode, exadata_model, rack_size, llm_selector],
323
+ outputs=[output, health, rating, retry_status]
324
+ )
325
+
326
+ # PDF Export
327
+ pdf_button = gr.Button("πŸ“„ Generate PDF")
328
+ pdf_file = gr.File(label="Download PDF", type="file")
329
+ pdf_button.click(
330
+ fn=generate_pdf,
331
+ inputs=[output, health, rating, retry_status],
332
+ outputs=pdf_file
333
+ )
334
+
335
+
336
+ with gr.Tab("Compare AWRs from OCI"):
337
+ upload_file = gr.File(label="Upload AWR Report", file_types=[".html", ".txt"])
338
+ upload_status = gr.Textbox(label="Upload Status")
339
+ upload_file.upload(fn=upload_awr_file, inputs=upload_file, outputs=upload_status)
340
+
341
+ refresh_button = gr.Button("πŸ”ƒ Refresh File List")
342
+ file_multiselect = gr.Dropdown(choices=[], label="Select AWR Files", multiselect=True)
343
+ refresh_button.click(fn=lambda: gr.update(choices=list_awr_files()), outputs=file_multiselect)
344
+
345
+ llm_compare = gr.Dropdown(choices=list(supported_llms.keys()), value="gpt-4-turbo", label="LLM Model for Comparison")
346
+ compare_output = gr.Textbox(label="Comparison Output", lines=20)
347
+ gr.Button("Compare Selected AWRs").click(
348
+ fn=compare_awrs,
349
+ inputs=[file_multiselect, llm_compare],
350
+ outputs=compare_output
351
+ )
352
+
353
+ if __name__ == "__main__":
354
+ demo.launch(debug=True)
355
+
356
+
357
+