gaur3009 commited on
Commit
19dce95
Β·
verified Β·
1 Parent(s): e58e559

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +628 -0
app.py ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ import faiss
4
+ import torch
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM
6
+ from sentence_transformers import SentenceTransformer
7
+ import time
8
+
9
+ # -------------------------------
10
+ # Load dataset
11
+ # -------------------------------
12
+ file_path = "marketing-campaigns.csv" # file uploaded in Hugging Face space
13
+ df = pd.read_csv(file_path)
14
+ df = df.dropna() # drop completely empty rows
15
+ df["text"] = df.astype(str).agg(" | ".join, axis=1) # merge all cols into text
16
+
17
+ # -------------------------------
18
+ # Embeddings + FAISS
19
+ # -------------------------------
20
+ embed_model = SentenceTransformer("all-MiniLM-L6-v2")
21
+ embeddings = embed_model.encode(df["text"].tolist(), convert_to_tensor=True, show_progress_bar=True)
22
+ embeddings_np = embeddings.detach().cpu().numpy()
23
+
24
+ d = embeddings_np.shape[1]
25
+ index = faiss.IndexFlatL2(d)
26
+ index.add(embeddings_np)
27
+
28
+ # -------------------------------
29
+ # Load LLM (default Phi-4-mini)
30
+ # -------------------------------
31
+ model_choices = {
32
+ "openai/gpt-oss-120b": "microsoft/phi-4-mini",
33
+ "openai/gpt-oss-20b": "microsoft/phi-4-mini" # placeholder, can map to another HF model
34
+ }
35
+
36
+ current_model_id = "openai/gpt-oss-120b"
37
+ hf_model = model_choices[current_model_id]
38
+
39
+ tokenizer = AutoTokenizer.from_pretrained(hf_model)
40
+ model = AutoModelForCausalLM.from_pretrained(
41
+ hf_model, torch_dtype=torch.float32, device_map="auto"
42
+ )
43
+
44
+ # -------------------------------
45
+ # RAG Functions
46
+ # -------------------------------
47
+ def retrieve_context(query, k=3):
48
+ query_vec = embed_model.encode([query], convert_to_tensor=True).cpu().numpy()
49
+ D, I = index.search(query_vec, k)
50
+ results = [df.iloc[i]["text"] for i in I[0]]
51
+ return results
52
+
53
+ def generate_with_rag(prompt, temperature=0.8, max_tokens=250):
54
+ # Step 1: Retrieve context
55
+ context = retrieve_context(prompt, k=3)
56
+ context_str = "\n".join(context)
57
+
58
+ # Step 2: Construct grounded prompt
59
+ rag_prompt = f"""
60
+ You are a creative AI campaign assistant.
61
+ Use the following supporting data to ground your answer:
62
+ {context_str}
63
+
64
+ Task: Generate a unique and creative marketing campaign idea for:
65
+ {prompt}
66
+ """
67
+
68
+ # Step 3: Generate
69
+ inputs = tokenizer(rag_prompt, return_tensors="pt").to(model.device)
70
+ outputs = model.generate(
71
+ **inputs,
72
+ max_length=max_tokens,
73
+ temperature=temperature,
74
+ top_p=0.9
75
+ )
76
+ return tokenizer.decode(outputs[0], skip_special_tokens=True)
77
+
78
+ def switch_model(model_choice):
79
+ """Switch between available models."""
80
+ global model, tokenizer, current_model_id
81
+ hf_model = model_choices.get(model_choice, "microsoft/phi-4-mini")
82
+ tokenizer = AutoTokenizer.from_pretrained(hf_model)
83
+ model = AutoModelForCausalLM.from_pretrained(
84
+ hf_model, torch_dtype=torch.float32, device_map="auto"
85
+ )
86
+ current_model_id = model_choice
87
+ return gr.update(visible=(model_choice=="openai/gpt-oss-120b")), gr.update(visible=(model_choice=="openai/gpt-oss-20b")), model_choice
88
+
89
+ # -------------------------------
90
+ # Custom CSS with enterprise styling
91
+ # -------------------------------
92
+ custom_css = """
93
+ :root {
94
+ --primary: #2563eb;
95
+ --primary-dark: #1d4ed8;
96
+ --secondary: #64748b;
97
+ --dark: #1e293b;
98
+ --light: #f8fafc;
99
+ --success: #10b981;
100
+ --warning: #f59e0b;
101
+ --danger: #ef4444;
102
+ --gray-100: #f1f5f9;
103
+ --gray-200: #e2e8f0;
104
+ --gray-300: #cbd5e1;
105
+ --gray-700: #334155;
106
+ --radius: 12px;
107
+ --shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
108
+ }
109
+
110
+ .gradio-container {
111
+ font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
112
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
113
+ min-height: 100vh;
114
+ padding: 20px;
115
+ }
116
+
117
+ .main-container {
118
+ background-color: rgba(255, 255, 255, 0.95);
119
+ backdrop-filter: blur(10px);
120
+ border-radius: var(--radius);
121
+ padding: 24px;
122
+ box-shadow: var(--shadow);
123
+ border: 1px solid rgba(255, 255, 255, 0.18);
124
+ margin-bottom: 20px;
125
+ }
126
+
127
+ .dark .main-container {
128
+ background-color: rgba(30, 41, 59, 0.95);
129
+ color: var(--light);
130
+ }
131
+
132
+ .header {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ padding: 16px 0;
137
+ margin-bottom: 24px;
138
+ border-bottom: 1px solid var(--gray-200);
139
+ }
140
+
141
+ .dark .header {
142
+ border-bottom: 1px solid var(--gray-700);
143
+ }
144
+
145
+ .logo {
146
+ display: flex;
147
+ align-items: center;
148
+ gap: 12px;
149
+ font-weight: 700;
150
+ font-size: 24px;
151
+ color: var(--dark);
152
+ }
153
+
154
+ .dark .logo {
155
+ color: var(--light);
156
+ }
157
+
158
+ .logo-icon {
159
+ background: var(--primary);
160
+ color: white;
161
+ width: 40px;
162
+ height: 40px;
163
+ border-radius: 8px;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ }
168
+
169
+ .nav-links {
170
+ display: flex;
171
+ gap: 24px;
172
+ }
173
+
174
+ .nav-link {
175
+ color: var(--secondary);
176
+ text-decoration: none;
177
+ font-weight: 500;
178
+ transition: color 0.2s;
179
+ }
180
+
181
+ .nav-link:hover, .nav-link.active {
182
+ color: var(--primary);
183
+ }
184
+
185
+ .dark .nav-link {
186
+ color: var(--gray-300);
187
+ }
188
+
189
+ .dark .nav-link:hover, .dark .nav-link.active {
190
+ color: var(--primary);
191
+ }
192
+
193
+ .badge-container {
194
+ margin-bottom: 24px;
195
+ }
196
+
197
+ h1, h2, h3, h4 {
198
+ color: var(--dark);
199
+ font-weight: 700;
200
+ }
201
+
202
+ .dark h1, .dark h2, .dark h3, .dark h4 {
203
+ color: var(--light);
204
+ }
205
+
206
+ .label {
207
+ font-weight: 600;
208
+ color: var(--dark);
209
+ margin-bottom: 8px;
210
+ }
211
+
212
+ .dark .label {
213
+ color: var(--light);
214
+ }
215
+
216
+ input, textarea {
217
+ border-radius: var(--radius) !important;
218
+ padding: 12px 16px !important;
219
+ border: 1px solid var(--gray-300) !important;
220
+ background: white !important;
221
+ }
222
+
223
+ .dark input, .dark textarea {
224
+ border: 1px solid var(--gray-700) !important;
225
+ background: var(--dark) !important;
226
+ color: var(--light) !important;
227
+ }
228
+
229
+ button {
230
+ border-radius: var(--radius) !important;
231
+ padding: 12px 24px !important;
232
+ font-weight: 600 !important;
233
+ transition: all 0.2s !important;
234
+ border: none !important;
235
+ }
236
+
237
+ button.primary {
238
+ background: var(--primary) !important;
239
+ color: white !important;
240
+ }
241
+
242
+ button.primary:hover {
243
+ background: var(--primary-dark) !important;
244
+ transform: translateY(-2px);
245
+ }
246
+
247
+ .dropdown {
248
+ border-radius: var(--radius) !important;
249
+ }
250
+
251
+ .accordion {
252
+ border-radius: var(--radius) !important;
253
+ border: 1px solid var(--gray-200) !important;
254
+ }
255
+
256
+ .dark .accordion {
257
+ border: 1px solid var(--gray-700) !important;
258
+ }
259
+
260
+ .footer {
261
+ text-align: center;
262
+ padding: 24px 0;
263
+ margin-top: 40px;
264
+ color: var(--secondary);
265
+ font-size: 14px;
266
+ }
267
+
268
+ .dark .footer {
269
+ color: var(--gray-300);
270
+ }
271
+
272
+ .stats {
273
+ display: flex;
274
+ gap: 24px;
275
+ margin: 20px 0;
276
+ }
277
+
278
+ .stat-card {
279
+ background: white;
280
+ padding: 16px;
281
+ border-radius: var(--radius);
282
+ box-shadow: var(--shadow);
283
+ flex: 1;
284
+ text-align: center;
285
+ }
286
+
287
+ .dark .stat-card {
288
+ background: var(--dark);
289
+ }
290
+
291
+ .stat-value {
292
+ font-size: 24px;
293
+ font-weight: 700;
294
+ color: var(--primary);
295
+ }
296
+
297
+ .stat-label {
298
+ font-size: 14px;
299
+ color: var(--secondary);
300
+ }
301
+
302
+ .processing-bar {
303
+ height: 4px;
304
+ width: 100%;
305
+ background: var(--gray-200);
306
+ border-radius: 2px;
307
+ overflow: hidden;
308
+ margin: 16px 0;
309
+ }
310
+
311
+ .progress {
312
+ height: 100%;
313
+ background: var(--primary);
314
+ width: 0%;
315
+ transition: width 0.4s;
316
+ }
317
+
318
+ @keyframes pulse {
319
+ 0% { opacity: 1; }
320
+ 50% { opacity: 0.5; }
321
+ 100% { opacity: 1; }
322
+ }
323
+
324
+ .pulse {
325
+ animation: pulse 2s infinite;
326
+ }
327
+
328
+ .model-badge {
329
+ display: inline-block;
330
+ padding: 4px 12px;
331
+ background: var(--gray-100);
332
+ color: var(--gray-700);
333
+ border-radius: 20px;
334
+ font-size: 12px;
335
+ font-weight: 600;
336
+ margin-left: 12px;
337
+ }
338
+
339
+ .dark .model-badge {
340
+ background: var(--gray-700);
341
+ color: var(--gray-200);
342
+ }
343
+
344
+ .tabs {
345
+ display: flex;
346
+ gap: 8px;
347
+ margin-bottom: 20px;
348
+ }
349
+
350
+ .tab {
351
+ padding: 8px 16px;
352
+ background: var(--gray-100);
353
+ border-radius: var(--radius);
354
+ cursor: pointer;
355
+ font-weight: 500;
356
+ }
357
+
358
+ .tab.active {
359
+ background: var(--primary);
360
+ color: white;
361
+ }
362
+
363
+ .dark .tab {
364
+ background: var(--gray-700);
365
+ color: var(--gray-200);
366
+ }
367
+
368
+ .dark .tab.active {
369
+ background: var(--primary);
370
+ color: white;
371
+ }
372
+ """
373
+
374
+ # -------------------------------
375
+ # Custom JavaScript
376
+ # -------------------------------
377
+ js = """
378
+ <script>
379
+ // Wait for the page to load
380
+ document.addEventListener('DOMContentLoaded', function() {
381
+ // Add header navigation
382
+ const headerHtml = `
383
+ <div class="header">
384
+ <div class="logo">
385
+ <div class="logo-icon">AI</div>
386
+ <span>CampaignForge</span>
387
+ </div>
388
+ <div class="nav-links">
389
+ <a href="#" class="nav-link active">Home</a>
390
+ <a href="#" class="nav-link">Campaigns</a>
391
+ <a href="#" class="nav-link">Analytics</a>
392
+ <a href="#" class="nav-link">Settings</a>
393
+ </div>
394
+ </div>
395
+ `;
396
+
397
+ // Add stats cards
398
+ const statsHtml = `
399
+ <div class="stats">
400
+ <div class="stat-card">
401
+ <div class="stat-value">${Math.floor(Math.random() * 1000) + 500}</div>
402
+ <div class="stat-label">Campaigns Generated</div>
403
+ </div>
404
+ <div class="stat-card">
405
+ <div class="stat-value">${Math.floor(Math.random() * 50) + 95}%</div>
406
+ <div class="stat-label">Success Rate</div>
407
+ </div>
408
+ <div class="stat-card">
409
+ <div class="stat-value">${Math.floor(Math.random() * 20) + 5}</div>
410
+ <div class="stat-label">Active Models</div>
411
+ </div>
412
+ </div>
413
+ `;
414
+
415
+ // Add footer
416
+ const footerHtml = `
417
+ <div class="footer">
418
+ <p>Β© 2023 CampaignForge AI β€’ Enterprise Marketing Solution</p>
419
+ <p>Powered by RAG Technology and Advanced Language Models</p>
420
+ </div>
421
+ `;
422
+
423
+ // Insert the header at the top of the main container
424
+ const mainContainer = document.querySelector('.gradio-container > div:first-child');
425
+ if (mainContainer) {
426
+ mainContainer.insertAdjacentHTML('afterbegin', headerHtml);
427
+ mainContainer.insertAdjacentHTML('beforeend', footerHtml);
428
+
429
+ // Find the first main-container and insert stats after it
430
+ const firstMainContainer = document.querySelector('.main-container');
431
+ if (firstMainContainer) {
432
+ firstMainContainer.insertAdjacentHTML('afterend', statsHtml);
433
+ }
434
+ }
435
+
436
+ // Add processing animation to generate button
437
+ const generateBtn = document.querySelector('button.primary');
438
+ if (generateBtn) {
439
+ generateBtn.addEventListener('click', function() {
440
+ const outputBox = document.querySelector('[data-testid="textbox"]');
441
+ if (outputBox) {
442
+ outputBox.innerHTML = '<div class="processing-bar"><div class="progress" id="progress-bar"></div></div><p class="pulse">Generating your campaign... This may take a moment</p>';
443
+
444
+ let width = 0;
445
+ const interval = setInterval(function() {
446
+ if (width >= 90) {
447
+ clearInterval(interval);
448
+ } else {
449
+ width += 5;
450
+ const progressBar = document.getElementById('progress-bar');
451
+ if (progressBar) {
452
+ progressBar.style.width = width + '%';
453
+ }
454
+ }
455
+ }, 200);
456
+ }
457
+ });
458
+ }
459
+
460
+ // Add tab navigation
461
+ const tabsHtml = `
462
+ <div class="tabs">
463
+ <div class="tab active" data-tab="generate">Generate Campaign</div>
464
+ <div class="tab" data-tab="history">Campaign History</div>
465
+ <div class="tab" data-tab="settings">Settings</div>
466
+ </div>
467
+ `;
468
+
469
+ const inputArea = document.querySelector('.main-container .gr-box');
470
+ if (inputArea) {
471
+ inputArea.insertAdjacentHTML('beforebegin', tabsHtml);
472
+
473
+ // Add tab click handlers
474
+ const tabs = document.querySelectorAll('.tab');
475
+ tabs.forEach(tab => {
476
+ tab.addEventListener('click', function() {
477
+ tabs.forEach(t => t.classList.remove('active'));
478
+ this.classList.add('active');
479
+
480
+ // For demo purposes, just show an alert
481
+ alert('Switching to ' + this.textContent + ' tab. This would load different content in a real application.');
482
+ });
483
+ });
484
+ }
485
+
486
+ // Add dark mode toggle
487
+ const darkModeToggle = document.createElement('button');
488
+ darkModeToggle.textContent = 'πŸŒ™ Dark Mode';
489
+ darkModeToggle.classList.add('secondary');
490
+ darkModeToggle.style.marginLeft = 'auto';
491
+ darkModeToggle.style.marginRight = '20px';
492
+
493
+ darkModeToggle.addEventListener('click', function() {
494
+ document.body.classList.toggle('dark');
495
+ this.textContent = document.body.classList.contains('dark') ? 'β˜€οΈ Light Mode' : 'πŸŒ™ Dark Mode';
496
+ });
497
+
498
+ const header = document.querySelector('.header');
499
+ if (header) {
500
+ header.appendChild(darkModeToggle);
501
+ }
502
+
503
+ // Add model indicator to output
504
+ const outputLabel = document.querySelector('[aria-label="Generated Campaign Script"]');
505
+ if (outputLabel) {
506
+ const modelName = document.querySelector('.gr-dropdown__select')?.value || 'openai/gpt-oss-120b';
507
+ const modelBadge = document.createElement('span');
508
+ modelBadge.classList.add('model-badge');
509
+ modelBadge.textContent = modelName;
510
+ outputLabel.appendChild(modelBadge);
511
+ }
512
+
513
+ // Update model badge when model changes
514
+ const modelDropdown = document.querySelector('.gr-dropdown__select');
515
+ if (modelDropdown) {
516
+ modelDropdown.addEventListener('change', function() {
517
+ const modelBadge = document.querySelector('.model-badge');
518
+ if (modelBadge) {
519
+ modelBadge.textContent = this.value;
520
+ }
521
+ });
522
+ }
523
+ });
524
+ </script>
525
+ """
526
+
527
+ # -------------------------------
528
+ # Gradio UI
529
+ # -------------------------------
530
+ with gr.Blocks(fill_height=True, theme=gr.themes.Soft(), css=custom_css) as demo:
531
+ # Add custom JavaScript
532
+ gr.HTML(js)
533
+
534
+ # Top badges
535
+ with gr.Row(elem_classes="badge-container"):
536
+ gr.HTML("""
537
+ <div style="display: flex; justify-content: center; align-items: center; gap: 15px; flex-wrap: wrap;">
538
+ <a href="https://huggingface.co/spaces/VIDraft/gpt-oss-RAG" target="_blank">
539
+ <img src="https://img.shields.io/static/v1?label=gpt-oss-20b&message=RAG&color=%23000080&labelColor=%23ffa500&logo=huggingface&logoColor=white&style=for-the-badge" alt="badge">
540
+ </a>
541
+ <a href="https://discord.gg/openfreeai" target="_blank">
542
+ <img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%23000080&labelColor=%23ffa500&logo=discord&logoColor=white&style=for-the-badge" alt="badge">
543
+ </a>
544
+ <a href="#" target="_blank">
545
+ <img src="https://img.shields.io/static/v1?label=Enterprise&message=Grade&color=%23000080&labelColor=%23ffa500&logo=azurepipelines&logoColor=white&style=for-the-badge" alt="badge">
546
+ </a>
547
+ </div>
548
+ """)
549
+
550
+ with gr.Row():
551
+ # Sidebar
552
+ with gr.Column(scale=1):
553
+ with gr.Group(elem_classes="main-container"):
554
+ gr.Markdown("## πŸš€ Inference Provider")
555
+ model_dropdown = gr.Dropdown(
556
+ choices=list(model_choices.keys()),
557
+ value=current_model_id,
558
+ label="πŸ“Š Select Model"
559
+ )
560
+ reload_btn = gr.Button("πŸ”„ Apply Model Change", variant="primary", elem_classes="primary")
561
+
562
+ with gr.Accordion("βš™οΈ Advanced Options", open=False):
563
+ temperature = gr.Slider(
564
+ minimum=0, maximum=2, value=0.8, step=0.1, label="Temperature"
565
+ )
566
+ max_tokens = gr.Slider(
567
+ minimum=50, maximum=1024, value=250, step=10, label="Max Tokens"
568
+ )
569
+
570
+ gr.Markdown("---")
571
+ gr.Markdown("### πŸ“ˆ Recent Activity")
572
+ gr.HTML("""
573
+ <div style="font-size: 14px; line-height: 1.6;">
574
+ <p>βœ… Campaign for Tech Startup - 2 hours ago</p>
575
+ <p>βœ… Holiday Promotion - 5 hours ago</p>
576
+ <p>βœ… Product Launch - Yesterday</p>
577
+ </div>
578
+ """)
579
+
580
+ # Main chat area
581
+ with gr.Column(scale=3):
582
+ with gr.Group(elem_classes="main-container"):
583
+ gr.Markdown("## πŸ’¬ RAG-powered Creative Campaign Assistant")
584
+ gr.Markdown("Generate compelling marketing campaigns using our AI-powered platform with retrieval-augmented generation technology.")
585
+
586
+ query = gr.Textbox(label="Enter campaign idea / theme", lines=2, placeholder="e.g., A summer promotion for eco-friendly water bottles targeting millennials")
587
+ output = gr.Textbox(label="Generated Campaign Script", lines=10)
588
+
589
+ btn = gr.Button("✨ Generate with RAG", elem_classes="primary")
590
+
591
+ btn.click(
592
+ generate_with_rag,
593
+ inputs=[query, temperature, max_tokens],
594
+ outputs=output
595
+ )
596
+
597
+ with gr.Column(visible=True) as model_120b_container:
598
+ gr.Markdown("### Active Model: openai/gpt-oss-120b")
599
+ with gr.Column(visible=False) as model_20b_container:
600
+ gr.Markdown("### Active Model: openai/gpt-oss-20b")
601
+
602
+ with gr.Group(elem_classes="main-container"):
603
+ gr.Markdown("## πŸ“Š Campaign Performance")
604
+ gr.HTML("""
605
+ <div style="display: flex; justify-content: space-between; margin: 15px 0;">
606
+ <div style="text-align: center;">
607
+ <div style="font-size: 24px; font-weight: bold; color: #2563eb;">87%</div>
608
+ <div style="font-size: 14px;">Engagement Rate</div>
609
+ </div>
610
+ <div style="text-align: center;">
611
+ <div style="font-size: 24px; font-weight: bold; color: #10b981;">2.4x</div>
612
+ <div style="font-size: 14px;">ROI Improvement</div>
613
+ </div>
614
+ <div style="text-align: center;">
615
+ <div style="font-size: 24px; font-weight: bold; color: #f59e0b;">14%</div>
616
+ <div style="font-size: 14px;">Conversion Rate</div>
617
+ </div>
618
+ </div>
619
+ """)
620
+
621
+ reload_btn.click(
622
+ fn=switch_model,
623
+ inputs=[model_dropdown],
624
+ outputs=[model_120b_container, model_20b_container, gr.State(current_model_id)]
625
+ )
626
+
627
+ if __name__ == "__main__":
628
+ demo.launch()