AYI-NEDJIMI commited on
Commit
f265d87
·
verified ·
1 Parent(s): a971997

Upload folder using huggingface_hub

Browse files
Files changed (4) hide show
  1. .gitignore +31 -0
  2. README.md +108 -5
  3. app.py +504 -0
  4. requirements.txt +5 -0
.gitignore ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+ .env
22
+ .venv
23
+ venv/
24
+ ENV/
25
+ .DS_Store
26
+ .idea/
27
+ .vscode/
28
+ *.swp
29
+ *.swo
30
+ *~
31
+ flagged/
README.md CHANGED
@@ -1,12 +1,115 @@
1
  ---
2
  title: Model Playground
3
- emoji: 🐢
4
- colorFrom: indigo
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
 
 
 
 
 
 
 
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: Model Playground
3
+ emoji: 🎮
4
+ colorFrom: red
5
+ colorTo: purple
6
  sdk: gradio
7
+ sdk_version: 4.44.0
8
  app_file: app.py
9
  pinned: false
10
+ license: apache-2.0
11
+ tags:
12
+ - cybersecurity
13
+ - ISO27001
14
+ - GDPR
15
+ - RGPD
16
+ - model-comparison
17
+ - fine-tuning
18
+ - qwen
19
  ---
20
 
21
+ # Model Playground
22
+
23
+ Interactive playground for experimenting with 3 fine-tuned cybersecurity AI models.
24
+
25
+ ## Features
26
+
27
+ ### Single-Turn Q&A Mode
28
+ - Select from 3 specialized models (ISO27001, RGPD, CyberSec-3B)
29
+ - Editable system prompt with model-specific defaults
30
+ - Adjustable generation parameters:
31
+ - **Temperature** (0-2, step 0.1): Controls randomness
32
+ - **Top-p** (0-1, step 0.05): Nucleus sampling threshold
33
+ - **Top-k** (0-100, step 5): Top-k sampling (0 = disabled)
34
+ - **Max tokens** (128-2048, step 128): Maximum response length
35
+ - **Repetition penalty** (1.0-2.0, step 0.1): Reduces repetition
36
+ - Real-time generation metrics:
37
+ - Token count
38
+ - Generation time (seconds)
39
+ - Tokens per second
40
+ - Export prompt + response as JSON
41
+
42
+ ### Side-by-Side Comparison Mode
43
+ - Compare responses from the same model with 2 different parameter configurations
44
+ - Separate metrics for each configuration
45
+ - Perfect for A/B testing prompt engineering strategies
46
+
47
+ ### Dark Theme
48
+ - Optimized dark theme for comfortable extended use
49
+ - Clean, minimal interface focused on content
50
+
51
+ ## Models
52
+
53
+ | Model | Specialty | Base Model | Size |
54
+ |-------|-----------|------------|------|
55
+ | **ISO27001-Expert-1.5B** | ISO/IEC 27001 ISMS | Qwen 2.5 1.5B Instruct | 1.5B |
56
+ | **RGPD-Expert-1.5B** | GDPR/RGPD compliance | Qwen 2.5 1.5B Instruct | 1.5B |
57
+ | **CyberSec-Assistant-3B** | General cybersecurity | Qwen 2.5 3B Instruct | 3B |
58
+
59
+ All models are fine-tuned using QLoRA on domain-specific cybersecurity datasets.
60
+
61
+ ## Usage Tips
62
+
63
+ ### Temperature Settings
64
+ - **0.0-0.3**: Highly deterministic, factual responses (best for compliance questions)
65
+ - **0.4-0.7**: Balanced creativity and accuracy (recommended default)
66
+ - **0.8-1.5**: More creative and diverse responses
67
+ - **1.6-2.0**: Very creative, potentially less coherent
68
+
69
+ ### Top-p and Top-k
70
+ - Use **Top-p** for dynamic vocabulary filtering (recommended: 0.9-0.95)
71
+ - Use **Top-k** for fixed vocabulary filtering (recommended: 40-50, or 0 to disable)
72
+ - Generally, use one or the other, not both aggressively
73
+
74
+ ### Repetition Penalty
75
+ - **1.0**: No penalty
76
+ - **1.1-1.3**: Gentle reduction of repetition (recommended)
77
+ - **1.4+**: Strong penalty, may affect response quality
78
+
79
+ ## Example Prompts
80
+
81
+ **ISO27001 Expert:**
82
+ - "What are the mandatory clauses of ISO 27001:2022?"
83
+ - "How do I conduct a risk assessment according to ISO 27001?"
84
+ - "What changed between ISO 27001:2013 and ISO 27001:2022?"
85
+
86
+ **RGPD Expert:**
87
+ - "What are the 6 lawful bases for processing under GDPR?"
88
+ - "When is a Data Protection Impact Assessment (DPIA) required?"
89
+ - "What are the penalties for GDPR non-compliance?"
90
+
91
+ **CyberSec Assistant:**
92
+ - "Explain the MITRE ATT&CK framework"
93
+ - "What are the main requirements of the NIS2 directive?"
94
+ - "How do I set up a SOC from scratch?"
95
+
96
+ ## Technical Details
97
+
98
+ - **Framework**: Gradio 4.44.0
99
+ - **Model Loading**: Dynamic loading with thread-safe caching
100
+ - **Inference**: CPU-based (float32) for broader compatibility
101
+ - **LoRA**: PEFT adapters loaded on top of base Qwen 2.5 models
102
+ - **Generation**: Greedy or sampling based on temperature setting
103
+
104
+ ## Links
105
+
106
+ - **Creator**: [Ayi NEDJIMI](https://huggingface.co/AYI-NEDJIMI)
107
+ - **Models**:
108
+ - [ISO27001-Expert-1.5B](https://huggingface.co/AYI-NEDJIMI/ISO27001-Expert-1.5B)
109
+ - [RGPD-Expert-1.5B](https://huggingface.co/AYI-NEDJIMI/RGPD-Expert-1.5B)
110
+ - [CyberSec-Assistant-3B](https://huggingface.co/AYI-NEDJIMI/CyberSec-Assistant-3B)
111
+ - **Full Portfolio**: [CyberSec AI Collection](https://huggingface.co/collections/AYI-NEDJIMI/cybersec-ai-portfolio-datasets-models-and-spaces-699224074a478ec0feeac493)
112
+
113
+ ## License
114
+
115
+ Apache 2.0
app.py ADDED
@@ -0,0 +1,504 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gc
2
+ import json
3
+ import os
4
+ import threading
5
+ import time
6
+ from typing import Optional
7
+
8
+ import gradio as gr
9
+ import torch
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer
11
+ from peft import PeftModel
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Model registry
15
+ # ---------------------------------------------------------------------------
16
+ MODELS = {
17
+ "ISO27001-Expert-1.5B": {
18
+ "base": "Qwen/Qwen2.5-1.5B-Instruct",
19
+ "adapter": "AYI-NEDJIMI/ISO27001-Expert-1.5B",
20
+ "default_prompt": (
21
+ "You are ISO 27001 Expert, a specialized AI assistant for "
22
+ "ISO/IEC 27001 information security management systems. "
23
+ "You help organizations understand, implement, and maintain "
24
+ "ISO 27001 certification, including risk assessment, controls "
25
+ "from Annex A, Statement of Applicability, and audit preparation."
26
+ ),
27
+ },
28
+ "RGPD-Expert-1.5B": {
29
+ "base": "Qwen/Qwen2.5-1.5B-Instruct",
30
+ "adapter": "AYI-NEDJIMI/RGPD-Expert-1.5B",
31
+ "default_prompt": (
32
+ "You are RGPD Expert, a specialized AI assistant for GDPR/RGPD "
33
+ "data protection regulations. You help organizations understand "
34
+ "their obligations under the General Data Protection Regulation, "
35
+ "including data subject rights, Data Protection Impact Assessments, "
36
+ "lawful bases for processing, and breach notification procedures."
37
+ ),
38
+ },
39
+ "CyberSec-Assistant-3B": {
40
+ "base": "Qwen/Qwen2.5-3B-Instruct",
41
+ "adapter": "AYI-NEDJIMI/CyberSec-Assistant-3B",
42
+ "default_prompt": (
43
+ "You are CyberSec Assistant, an expert AI specialized in "
44
+ "cybersecurity, compliance (GDPR, NIS2, DORA, AI Act, ISO 27001), "
45
+ "penetration testing, SOC operations, and AI security."
46
+ ),
47
+ },
48
+ }
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Global model state
52
+ # ---------------------------------------------------------------------------
53
+ _lock = threading.Lock()
54
+ _loaded_model_name = None
55
+ _tokenizer = None
56
+ _model = None
57
+
58
+
59
+ def load_model(model_name: str):
60
+ """Load or switch to a different model."""
61
+ global _loaded_model_name, _tokenizer, _model
62
+
63
+ with _lock:
64
+ if _loaded_model_name == model_name and _model is not None:
65
+ return # Already loaded
66
+
67
+ # Unload previous model
68
+ if _model is not None:
69
+ del _model
70
+ del _tokenizer
71
+ gc.collect()
72
+ torch.cuda.empty_cache()
73
+
74
+ # Load new model
75
+ cfg = MODELS[model_name]
76
+ hf_token = os.getenv("HF_TOKEN")
77
+
78
+ _tokenizer = AutoTokenizer.from_pretrained(
79
+ cfg["base"],
80
+ trust_remote_code=True,
81
+ token=hf_token,
82
+ )
83
+
84
+ base = AutoModelForCausalLM.from_pretrained(
85
+ cfg["base"],
86
+ torch_dtype=torch.float32,
87
+ device_map="cpu",
88
+ trust_remote_code=True,
89
+ token=hf_token,
90
+ )
91
+
92
+ _model = PeftModel.from_pretrained(
93
+ base,
94
+ cfg["adapter"],
95
+ torch_dtype=torch.float32,
96
+ token=hf_token,
97
+ )
98
+ _model.eval()
99
+
100
+ _loaded_model_name = model_name
101
+
102
+
103
+ def generate_single(
104
+ model_name: str,
105
+ system_prompt: str,
106
+ user_prompt: str,
107
+ temperature: float,
108
+ top_p: float,
109
+ top_k: int,
110
+ max_tokens: int,
111
+ repetition_penalty: float,
112
+ ) -> tuple[str, dict]:
113
+ """
114
+ Generate a single response with metrics.
115
+ Returns: (response_text, metrics_dict)
116
+ """
117
+ if not user_prompt.strip():
118
+ return "", {}
119
+
120
+ # Load model
121
+ try:
122
+ load_model(model_name)
123
+ except Exception as e:
124
+ return f"Error loading model: {e}", {}
125
+
126
+ # Build messages
127
+ messages = [
128
+ {"role": "system", "content": system_prompt},
129
+ {"role": "user", "content": user_prompt},
130
+ ]
131
+
132
+ input_text = _tokenizer.apply_chat_template(
133
+ messages, tokenize=False, add_generation_prompt=True
134
+ )
135
+ inputs = _tokenizer(input_text, return_tensors="pt").to("cpu")
136
+ input_length = inputs.input_ids.shape[1]
137
+
138
+ # Generation
139
+ start_time = time.time()
140
+
141
+ with torch.no_grad():
142
+ outputs = _model.generate(
143
+ **inputs,
144
+ max_new_tokens=max_tokens,
145
+ temperature=temperature,
146
+ top_p=top_p,
147
+ top_k=top_k if top_k > 0 else None,
148
+ do_sample=temperature > 0,
149
+ repetition_penalty=repetition_penalty,
150
+ pad_token_id=_tokenizer.eos_token_id,
151
+ )
152
+
153
+ end_time = time.time()
154
+ elapsed = end_time - start_time
155
+
156
+ # Decode
157
+ generated_ids = outputs[0][input_length:]
158
+ response = _tokenizer.decode(generated_ids, skip_special_tokens=True)
159
+
160
+ # Metrics
161
+ num_tokens = len(generated_ids)
162
+ tokens_per_sec = num_tokens / elapsed if elapsed > 0 else 0
163
+
164
+ metrics = {
165
+ "tokens": num_tokens,
166
+ "time_sec": round(elapsed, 2),
167
+ "tokens_per_sec": round(tokens_per_sec, 2),
168
+ }
169
+
170
+ return response, metrics
171
+
172
+
173
+ # ---------------------------------------------------------------------------
174
+ # UI handlers
175
+ # ---------------------------------------------------------------------------
176
+ def generate_response(
177
+ model_name: str,
178
+ system_prompt: str,
179
+ user_prompt: str,
180
+ temperature: float,
181
+ top_p: float,
182
+ top_k: int,
183
+ max_tokens: int,
184
+ repetition_penalty: float,
185
+ ):
186
+ """Handler for single-turn Q&A."""
187
+ response, metrics = generate_single(
188
+ model_name, system_prompt, user_prompt,
189
+ temperature, top_p, top_k, max_tokens, repetition_penalty
190
+ )
191
+
192
+ metrics_text = ""
193
+ if metrics:
194
+ metrics_text = (
195
+ f"**Generation Metrics:**\n"
196
+ f"- Tokens: {metrics['tokens']}\n"
197
+ f"- Time: {metrics['time_sec']}s\n"
198
+ f"- Speed: {metrics['tokens_per_sec']} tokens/sec"
199
+ )
200
+
201
+ return response, metrics_text
202
+
203
+
204
+ def export_json(model_name: str, system_prompt: str, user_prompt: str, response: str, metrics_text: str):
205
+ """Export conversation as JSON."""
206
+ data = {
207
+ "model": model_name,
208
+ "system_prompt": system_prompt,
209
+ "user_prompt": user_prompt,
210
+ "response": response,
211
+ "metrics": metrics_text,
212
+ }
213
+ return json.dumps(data, indent=2, ensure_ascii=False)
214
+
215
+
216
+ def generate_comparison(
217
+ model_name: str,
218
+ system_prompt: str,
219
+ user_prompt: str,
220
+ # Config A
221
+ temp_a: float, top_p_a: float, top_k_a: int, max_tok_a: int, rep_pen_a: float,
222
+ # Config B
223
+ temp_b: float, top_p_b: float, top_k_b: int, max_tok_b: int, rep_pen_b: float,
224
+ ):
225
+ """Generate side-by-side comparison with different parameter sets."""
226
+
227
+ response_a, metrics_a = generate_single(
228
+ model_name, system_prompt, user_prompt,
229
+ temp_a, top_p_a, top_k_a, max_tok_a, rep_pen_a
230
+ )
231
+
232
+ response_b, metrics_b = generate_single(
233
+ model_name, system_prompt, user_prompt,
234
+ temp_b, top_p_b, top_k_b, max_tok_b, rep_pen_b
235
+ )
236
+
237
+ metrics_text_a = ""
238
+ if metrics_a:
239
+ metrics_text_a = (
240
+ f"**Config A Metrics:**\n"
241
+ f"- Tokens: {metrics_a['tokens']}\n"
242
+ f"- Time: {metrics_a['time_sec']}s\n"
243
+ f"- Speed: {metrics_a['tokens_per_sec']} tok/s"
244
+ )
245
+
246
+ metrics_text_b = ""
247
+ if metrics_b:
248
+ metrics_text_b = (
249
+ f"**Config B Metrics:**\n"
250
+ f"- Tokens: {metrics_b['tokens']}\n"
251
+ f"- Time: {metrics_b['time_sec']}s\n"
252
+ f"- Speed: {metrics_b['tokens_per_sec']} tok/s"
253
+ )
254
+
255
+ return response_a, metrics_text_a, response_b, metrics_text_b
256
+
257
+
258
+ def update_system_prompt(model_name: str):
259
+ """Update system prompt textbox when model changes."""
260
+ return MODELS[model_name]["default_prompt"]
261
+
262
+
263
+ # ---------------------------------------------------------------------------
264
+ # Gradio UI
265
+ # ---------------------------------------------------------------------------
266
+ DESCRIPTION = """\
267
+ ## Model Playground
268
+
269
+ Experiment with **3 fine-tuned cybersecurity models** using customizable parameters.
270
+
271
+ **Features:**
272
+ - Single-turn Q&A (no chat history)
273
+ - Adjustable generation parameters (temperature, top-p, top-k, max tokens, repetition penalty)
274
+ - Real-time generation metrics (tokens/sec, total time, token count)
275
+ - Export conversations as JSON
276
+ - Side-by-side comparison mode with 2 different parameter configurations
277
+ - Dark theme optimized for readability
278
+
279
+ **Models:**
280
+ - **ISO27001-Expert-1.5B**: ISO/IEC 27001 ISMS specialist
281
+ - **RGPD-Expert-1.5B**: GDPR/RGPD compliance expert
282
+ - **CyberSec-Assistant-3B**: General cybersecurity assistant
283
+ """
284
+
285
+ theme = gr.themes.Monochrome(
286
+ primary_hue="red",
287
+ secondary_hue="purple",
288
+ neutral_hue="slate",
289
+ font=gr.themes.GoogleFont("Inter"),
290
+ ).set(
291
+ body_background_fill="#0a0a0a",
292
+ body_background_fill_dark="#0a0a0a",
293
+ block_background_fill="#1a1a1a",
294
+ block_background_fill_dark="#1a1a1a",
295
+ input_background_fill="#262626",
296
+ input_background_fill_dark="#262626",
297
+ button_primary_background_fill="#dc2626",
298
+ button_primary_background_fill_dark="#dc2626",
299
+ )
300
+
301
+ with gr.Blocks(theme=theme, title="Model Playground") as demo:
302
+
303
+ gr.Markdown("# Model Playground")
304
+ gr.Markdown(DESCRIPTION)
305
+
306
+ with gr.Tabs():
307
+
308
+ # ===================================================================
309
+ # Tab 1: Single-Turn Q&A
310
+ # ===================================================================
311
+ with gr.Tab("Single-Turn Q&A"):
312
+ with gr.Row():
313
+ with gr.Column(scale=2):
314
+ model_select = gr.Dropdown(
315
+ choices=list(MODELS.keys()),
316
+ value="ISO27001-Expert-1.5B",
317
+ label="Select Model",
318
+ )
319
+ with gr.Column(scale=3):
320
+ system_prompt_box = gr.Textbox(
321
+ value=MODELS["ISO27001-Expert-1.5B"]["default_prompt"],
322
+ label="System Prompt (Editable)",
323
+ lines=4,
324
+ )
325
+
326
+ user_prompt_box = gr.Textbox(
327
+ label="Your Question",
328
+ placeholder="Enter your question here...",
329
+ lines=3,
330
+ )
331
+
332
+ with gr.Accordion("Generation Parameters", open=True):
333
+ with gr.Row():
334
+ temperature_slider = gr.Slider(
335
+ minimum=0, maximum=2, value=0.7, step=0.1,
336
+ label="Temperature",
337
+ info="Higher = more creative, lower = more deterministic"
338
+ )
339
+ top_p_slider = gr.Slider(
340
+ minimum=0, maximum=1, value=0.9, step=0.05,
341
+ label="Top-p (nucleus sampling)",
342
+ )
343
+ top_k_slider = gr.Slider(
344
+ minimum=0, maximum=100, value=50, step=5,
345
+ label="Top-k (0 = disabled)",
346
+ )
347
+
348
+ with gr.Row():
349
+ max_tokens_slider = gr.Slider(
350
+ minimum=128, maximum=2048, value=512, step=128,
351
+ label="Max Tokens",
352
+ )
353
+ repetition_penalty_slider = gr.Slider(
354
+ minimum=1.0, maximum=2.0, value=1.1, step=0.1,
355
+ label="Repetition Penalty",
356
+ )
357
+
358
+ generate_btn = gr.Button("Generate Response", variant="primary", size="lg")
359
+
360
+ with gr.Row():
361
+ with gr.Column(scale=3):
362
+ response_box = gr.Textbox(
363
+ label="Response",
364
+ lines=15,
365
+ interactive=False,
366
+ )
367
+ with gr.Column(scale=1):
368
+ metrics_box = gr.Markdown(label="Metrics")
369
+
370
+ with gr.Row():
371
+ export_btn = gr.Button("Export as JSON")
372
+ json_output = gr.Textbox(label="JSON Export", lines=10, visible=False)
373
+
374
+ # Wire up events
375
+ model_select.change(
376
+ fn=update_system_prompt,
377
+ inputs=[model_select],
378
+ outputs=[system_prompt_box],
379
+ )
380
+
381
+ generate_btn.click(
382
+ fn=generate_response,
383
+ inputs=[
384
+ model_select, system_prompt_box, user_prompt_box,
385
+ temperature_slider, top_p_slider, top_k_slider,
386
+ max_tokens_slider, repetition_penalty_slider,
387
+ ],
388
+ outputs=[response_box, metrics_box],
389
+ )
390
+
391
+ export_btn.click(
392
+ fn=export_json,
393
+ inputs=[model_select, system_prompt_box, user_prompt_box, response_box, metrics_box],
394
+ outputs=[json_output],
395
+ ).then(
396
+ fn=lambda: gr.update(visible=True),
397
+ outputs=[json_output],
398
+ )
399
+
400
+ gr.Examples(
401
+ examples=[
402
+ ["What are the mandatory clauses of ISO 27001:2022?"],
403
+ ["What are the 6 lawful bases for processing under GDPR?"],
404
+ ["Explain the MITRE ATT&CK framework."],
405
+ ["What are the main requirements of the NIS2 directive?"],
406
+ ],
407
+ inputs=user_prompt_box,
408
+ )
409
+
410
+ # ===================================================================
411
+ # Tab 2: Side-by-Side Comparison
412
+ # ===================================================================
413
+ with gr.Tab("Side-by-Side Comparison"):
414
+ gr.Markdown("### Compare responses from the same model with 2 different parameter configurations")
415
+
416
+ with gr.Row():
417
+ with gr.Column(scale=2):
418
+ model_select_comp = gr.Dropdown(
419
+ choices=list(MODELS.keys()),
420
+ value="ISO27001-Expert-1.5B",
421
+ label="Select Model",
422
+ )
423
+ with gr.Column(scale=3):
424
+ system_prompt_comp = gr.Textbox(
425
+ value=MODELS["ISO27001-Expert-1.5B"]["default_prompt"],
426
+ label="System Prompt (Editable)",
427
+ lines=4,
428
+ )
429
+
430
+ user_prompt_comp = gr.Textbox(
431
+ label="Your Question",
432
+ placeholder="Enter your question here...",
433
+ lines=3,
434
+ )
435
+
436
+ with gr.Row():
437
+ # Config A
438
+ with gr.Column():
439
+ gr.Markdown("#### Configuration A")
440
+ temp_a = gr.Slider(0, 2, value=0.3, step=0.1, label="Temperature")
441
+ top_p_a = gr.Slider(0, 1, value=0.9, step=0.05, label="Top-p")
442
+ top_k_a = gr.Slider(0, 100, value=50, step=5, label="Top-k")
443
+ max_tok_a = gr.Slider(128, 2048, value=512, step=128, label="Max Tokens")
444
+ rep_pen_a = gr.Slider(1.0, 2.0, value=1.1, step=0.1, label="Rep. Penalty")
445
+
446
+ # Config B
447
+ with gr.Column():
448
+ gr.Markdown("#### Configuration B")
449
+ temp_b = gr.Slider(0, 2, value=1.2, step=0.1, label="Temperature")
450
+ top_p_b = gr.Slider(0, 1, value=0.95, step=0.05, label="Top-p")
451
+ top_k_b = gr.Slider(0, 100, value=40, step=5, label="Top-k")
452
+ max_tok_b = gr.Slider(128, 2048, value=512, step=128, label="Max Tokens")
453
+ rep_pen_b = gr.Slider(1.0, 2.0, value=1.2, step=0.1, label="Rep. Penalty")
454
+
455
+ compare_btn = gr.Button("Generate Comparison", variant="primary", size="lg")
456
+
457
+ with gr.Row():
458
+ with gr.Column():
459
+ response_a = gr.Textbox(label="Response A", lines=12, interactive=False)
460
+ metrics_a = gr.Markdown()
461
+ with gr.Column():
462
+ response_b = gr.Textbox(label="Response B", lines=12, interactive=False)
463
+ metrics_b = gr.Markdown()
464
+
465
+ # Wire up events
466
+ model_select_comp.change(
467
+ fn=update_system_prompt,
468
+ inputs=[model_select_comp],
469
+ outputs=[system_prompt_comp],
470
+ )
471
+
472
+ compare_btn.click(
473
+ fn=generate_comparison,
474
+ inputs=[
475
+ model_select_comp, system_prompt_comp, user_prompt_comp,
476
+ temp_a, top_p_a, top_k_a, max_tok_a, rep_pen_a,
477
+ temp_b, top_p_b, top_k_b, max_tok_b, rep_pen_b,
478
+ ],
479
+ outputs=[response_a, metrics_a, response_b, metrics_b],
480
+ )
481
+
482
+ gr.Examples(
483
+ examples=[
484
+ ["What is a Data Protection Impact Assessment?"],
485
+ ["Explain the concept of Zero Trust security."],
486
+ ["What are the penalties for GDPR non-compliance?"],
487
+ ],
488
+ inputs=user_prompt_comp,
489
+ )
490
+
491
+ # Footer
492
+ gr.HTML("""
493
+ <div style="text-align:center; margin-top:2rem; padding-top:1rem; border-top:1px solid #333; color:#888; font-size:0.85rem;">
494
+ <p>Built by <a href="https://huggingface.co/AYI-NEDJIMI" style="color:#dc2626;">Ayi NEDJIMI</a>
495
+ | Models: <a href="https://huggingface.co/AYI-NEDJIMI/ISO27001-Expert-1.5B" style="color:#dc2626;">ISO27001</a>,
496
+ <a href="https://huggingface.co/AYI-NEDJIMI/RGPD-Expert-1.5B" style="color:#dc2626;">RGPD</a>,
497
+ <a href="https://huggingface.co/AYI-NEDJIMI/CyberSec-Assistant-3B" style="color:#dc2626;">CyberSec-3B</a>
498
+ | <a href="https://huggingface.co/collections/AYI-NEDJIMI/cybersec-ai-portfolio-datasets-models-and-spaces-699224074a478ec0feeac493" style="color:#dc2626;">Portfolio</a></p>
499
+ <p style="font-size:0.75rem; color:#666;">Fine-tuned with QLoRA on Qwen 2.5 | Model Playground</p>
500
+ </div>
501
+ """)
502
+
503
+ if __name__ == "__main__":
504
+ demo.launch()
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio==4.44.0
2
+ transformers==4.46.2
3
+ torch==2.5.1
4
+ peft==0.13.2
5
+ accelerate==1.1.1