AYI-NEDJIMI commited on
Commit
f5b81f8
·
verified ·
1 Parent(s): 3f7ad74

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +69 -11
  2. app.py +311 -117
  3. requirements.txt +4 -1
README.md CHANGED
@@ -1,6 +1,6 @@
1
  ---
2
- title: CyberSec Models Demo
3
- emoji: "\U0001F6E1\uFE0F"
4
  colorFrom: red
5
  colorTo: purple
6
  sdk: gradio
@@ -14,31 +14,89 @@ tags:
14
  - rgpd
15
  - gdpr
16
  - compliance
 
17
  - fine-tuned
 
18
  models:
19
  - AYI-NEDJIMI/CyberSec-Assistant-3B
20
  - AYI-NEDJIMI/ISO27001-Expert-1.5B
21
  - AYI-NEDJIMI/RGPD-Expert-1.5B
 
 
 
 
 
 
 
22
  ---
23
 
24
- # CyberSec AI Models Demo
25
 
26
- Interactive demo for three fine-tuned cybersecurity language models built on the Qwen2.5 architecture.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
  ## Models
29
 
30
  | Model | Base | Parameters | Specialty |
31
  |-------|------|------------|-----------|
32
- | **CyberSec Assistant 3B** | Qwen2.5-3B-Instruct | 3B | General cybersecurity, compliance, pentesting, SOC |
33
- | **ISO 27001 Expert 1.5B** | Qwen2.5-1.5B-Instruct | 1.5B | ISO/IEC 27001 ISMS |
34
- | **RGPD Expert 1.5B** | Qwen2.5-1.5B-Instruct | 1.5B | GDPR / RGPD data protection |
 
 
 
 
35
 
36
- All models are PEFT/LoRA adapters fine-tuned on domain-specific cybersecurity datasets.
 
 
 
 
37
 
38
- ## Usage
39
 
40
- Select a model from the dropdown and start chatting. The 1.5B models are loaded by default on CPU Spaces. The 3B model requires more memory and may be slow on free-tier CPU hardware.
 
 
 
 
 
 
 
 
 
 
41
 
42
  ## Author
43
 
44
- **AYI-NEDJIMI** [HuggingFace Profile](https://huggingface.co/AYI-NEDJIMI)
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: CyberSec Models - Advanced Demo
3
+ emoji: 🛡️
4
  colorFrom: red
5
  colorTo: purple
6
  sdk: gradio
 
14
  - rgpd
15
  - gdpr
16
  - compliance
17
+ - rag
18
  - fine-tuned
19
+ - streaming
20
  models:
21
  - AYI-NEDJIMI/CyberSec-Assistant-3B
22
  - AYI-NEDJIMI/ISO27001-Expert-1.5B
23
  - AYI-NEDJIMI/RGPD-Expert-1.5B
24
+ datasets:
25
+ - AYI-NEDJIMI/iso27001
26
+ - AYI-NEDJIMI/rgpd-fr
27
+ - AYI-NEDJIMI/gdpr-en
28
+ - AYI-NEDJIMI/mitre-attack-fr
29
+ - AYI-NEDJIMI/owasp-top10-fr
30
+ - AYI-NEDJIMI/nis2-directive-fr
31
  ---
32
 
33
+ # 🛡️ CyberSec AI Models - Advanced Demo
34
 
35
+ **Advanced interactive demo showcasing 3 fine-tuned cybersecurity AI models with RAG and streaming.**
36
+
37
+ ## Features
38
+
39
+ ### 💬 Chat Mode
40
+ - Select from 3 specialized models
41
+ - Enable RAG (Retrieval-Augmented Generation) for context from 80+ datasets
42
+ - Streaming responses (token-by-token generation)
43
+ - Adjustable temperature and max tokens
44
+ - Multi-turn conversations with full history
45
+
46
+ ### ⚖️ Compare Mode
47
+ - Ask the same question to all 3 models simultaneously
48
+ - See side-by-side responses
49
+ - Identify each model's strengths and specializations
50
+ - Compare with or without RAG
51
+
52
+ ### 🔍 RAG (Retrieval-Augmented Generation)
53
+ - Semantic search across 80+ cybersecurity datasets
54
+ - Top-k document retrieval using sentence-transformers
55
+ - Automatic context injection for more accurate, detailed answers
56
+ - Sources include: ISO 27001, RGPD/GDPR, MITRE ATT&CK, OWASP, NIS2, and more
57
 
58
  ## Models
59
 
60
  | Model | Base | Parameters | Specialty |
61
  |-------|------|------------|-----------|
62
+ | [ISO27001-Expert-1.5B](https://huggingface.co/AYI-NEDJIMI/ISO27001-Expert-1.5B) | Qwen2.5-1.5B-Instruct | 1.5B | ISO/IEC 27001 ISMS implementation, controls, auditing |
63
+ | [RGPD-Expert-1.5B](https://huggingface.co/AYI-NEDJIMI/RGPD-Expert-1.5B) | Qwen2.5-1.5B-Instruct | 1.5B | GDPR/RGPD compliance, data protection, DPO guidance |
64
+ | [CyberSec-Assistant-3B](https://huggingface.co/AYI-NEDJIMI/CyberSec-Assistant-3B) | Qwen2.5-3B-Instruct | 3B | General cybersecurity, pentesting, SOC, compliance |
65
+
66
+ All models are fine-tuned with **QLoRA (4-bit quantization)** on specialized cybersecurity datasets.
67
+
68
+ ## Technical Details
69
 
70
+ - **Fine-tuning method**: QLoRA (LoRA rank=64, alpha=128)
71
+ - **Training data**: 80+ bilingual (FR/EN) cybersecurity datasets
72
+ - **RAG embedding**: sentence-transformers/all-MiniLM-L6-v2
73
+ - **Inference**: CPU with float32 (Hugging Face free tier)
74
+ - **Streaming**: TextIteratorStreamer for real-time token generation
75
 
76
+ ## Use Cases
77
 
78
+ - **ISO 27001 compliance**: Implementation guidance, control selection, audit preparation
79
+ - **GDPR/RGPD compliance**: Data protection requirements, DPIA, breach notification
80
+ - **Cybersecurity research**: MITRE ATT&CK, OWASP, threat hunting, SOC operations
81
+ - **Training & education**: Interactive Q&A for cybersecurity professionals
82
+ - **Compliance assessment**: Compare regulatory frameworks (NIS2, DORA, AI Act)
83
+
84
+ ## Performance Notes
85
+
86
+ ⚠️ **Running on CPU**: First response takes 30-60 seconds while models load. Subsequent responses are faster but still slower than GPU inference.
87
+
88
+ 💡 **Tip**: The 1.5B models (ISO27001 and RGPD) are more responsive on CPU. The 3B model may be slower.
89
 
90
  ## Author
91
 
92
+ **Ayi NEDJIMI** - Senior Offensive Cybersecurity & AI Consultant
93
+
94
+ - 🌐 [Website](https://www.ayinedjimi-consultants.fr)
95
+ - 💼 [LinkedIn](https://www.linkedin.com/in/ayi-nedjimi)
96
+ - 🐙 [GitHub](https://github.com/ayinedjimi)
97
+ - 🐦 [Twitter/X](https://x.com/AyiNEDJIMI)
98
+ - 🤗 [HuggingFace Collection](https://huggingface.co/collections/AYI-NEDJIMI/cybersec-ai-portfolio-datasets-models-and-spaces-699224074a478ec0feeac493)
99
+
100
+ ## License
101
+
102
+ Apache 2.0
app.py CHANGED
@@ -1,11 +1,17 @@
1
  import gc
 
2
  import os
3
  import threading
 
 
4
 
5
  import gradio as gr
6
  import torch
7
- from transformers import AutoModelForCausalLM, AutoTokenizer
8
  from peft import PeftModel
 
 
 
9
 
10
  # ---------------------------------------------------------------------------
11
  # Model registry
@@ -27,7 +33,6 @@ MODELS = {
27
  "Explain the Statement of Applicability (SoA).",
28
  "What changed between ISO 27001:2013 and ISO 27001:2022?",
29
  ],
30
- "size_warning": None,
31
  },
32
  "RGPD Expert (1.5B)": {
33
  "base": "Qwen/Qwen2.5-1.5B-Instruct",
@@ -45,7 +50,6 @@ MODELS = {
45
  "Explain the right to data portability.",
46
  "What are the penalties for GDPR non-compliance?",
47
  ],
48
- "size_warning": None,
49
  },
50
  "CyberSec Assistant (3B)": {
51
  "base": "Qwen/Qwen2.5-3B-Instruct",
@@ -61,50 +65,104 @@ MODELS = {
61
  "Explain the NIS2 directive requirements.",
62
  "What are the OWASP Top 10 for 2024?",
63
  ],
64
- "size_warning": (
65
- "Warning: The 3B model requires ~6 GB RAM. On free CPU Spaces "
66
- "it may be slow or run out of memory. The 1.5B models are recommended."
67
- ),
68
  },
69
  }
70
 
71
- DEFAULT_MODEL = "ISO 27001 Expert (1.5B)"
72
-
73
  # ---------------------------------------------------------------------------
74
- # Global state
75
  # ---------------------------------------------------------------------------
76
- _lock = threading.Lock()
77
- _current_model_name: str | None = None
78
- _tokenizer = None
79
- _model = None
80
-
81
-
82
- def _unload_model():
83
- """Release the currently loaded model and free memory."""
84
- global _model, _tokenizer, _current_model_name
85
- _model = None
86
- _tokenizer = None
87
- _current_model_name = None
88
- gc.collect()
89
- if torch.cuda.is_available():
90
- torch.cuda.empty_cache()
91
-
92
 
93
- def _load_model(model_name: str):
94
- """Load base model + LoRA adapter. Call under _lock."""
95
- global _model, _tokenizer, _current_model_name
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
- if _current_model_name == model_name:
98
- return # already loaded
 
 
 
99
 
100
- _unload_model()
 
 
 
101
 
102
  cfg = MODELS[model_name]
103
- hf_token = os.getenv("HF_TOKEN", None)
104
 
105
- dtype = torch.float32 # CPU-safe default
106
-
107
- _tokenizer = AutoTokenizer.from_pretrained(
108
  cfg["base"],
109
  trust_remote_code=True,
110
  token=hf_token,
@@ -112,101 +170,149 @@ def _load_model(model_name: str):
112
 
113
  base = AutoModelForCausalLM.from_pretrained(
114
  cfg["base"],
115
- torch_dtype=dtype,
116
  device_map="cpu",
117
  trust_remote_code=True,
118
  token=hf_token,
119
  )
120
 
121
- _model = PeftModel.from_pretrained(
122
  base,
123
  cfg["adapter"],
124
- torch_dtype=dtype,
125
  token=hf_token,
126
  )
127
- _model.eval()
128
- _current_model_name = model_name
 
 
129
 
130
 
131
  # ---------------------------------------------------------------------------
132
- # Chat function
133
  # ---------------------------------------------------------------------------
134
- def respond(message: str, history: list[dict], model_name: str):
135
- """Generate a response from the selected model."""
 
 
 
 
 
 
 
 
136
  if not message.strip():
137
  yield ""
138
  return
139
 
140
  cfg = MODELS[model_name]
141
 
142
- # Show warning for large model
143
- if cfg["size_warning"]:
144
- yield f"*{cfg['size_warning']}*\n\nLoading model, please wait..."
145
- else:
146
- yield "Loading model, please wait..."
 
 
 
 
 
 
 
 
 
 
 
147
 
148
  with _lock:
149
  try:
150
- _load_model(model_name)
151
- except Exception as exc:
152
- yield f"**Error loading model:** {exc}"
153
  return
154
 
155
- # Build messages in ChatML style
156
- messages = [{"role": "system", "content": cfg["system_prompt"]}]
 
 
 
 
157
  for entry in history:
158
  messages.append({"role": entry["role"], "content": entry["content"]})
159
  messages.append({"role": "user", "content": message})
160
 
161
- input_text = _tokenizer.apply_chat_template(
162
  messages, tokenize=False, add_generation_prompt=True
163
  )
164
- inputs = _tokenizer(input_text, return_tensors="pt").to("cpu")
165
-
166
- with torch.no_grad():
167
- outputs = _model.generate(
168
- **inputs,
169
- max_new_tokens=512,
170
- temperature=0.7,
171
- top_p=0.9,
172
- do_sample=True,
173
- pad_token_id=_tokenizer.eos_token_id,
174
- )
175
-
176
- new_tokens = outputs[0][inputs["input_ids"].shape[-1]:]
177
- response = _tokenizer.decode(new_tokens, skip_special_tokens=True)
178
- yield response.strip()
179
-
180
-
181
- def update_examples(model_name: str):
182
- """Return example list for the selected model."""
183
- return gr.update(samples=[[q] for q in MODELS[model_name]["examples"]])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
 
185
 
186
  # ---------------------------------------------------------------------------
187
  # Gradio UI
188
  # ---------------------------------------------------------------------------
189
  DESCRIPTION = """\
190
- ## Interactive demo of three fine-tuned cybersecurity AI models
191
 
192
- | Model | Specialty |
193
- |-------|-----------|
194
- | **ISO 27001 Expert (1.5B)** | ISO/IEC 27001 ISMS implementation and auditing |
195
- | **RGPD Expert (1.5B)** | GDPR / RGPD data protection regulations |
196
- | **CyberSec Assistant (3B)** | General cybersecurity, compliance, pentesting, SOC |
197
 
198
- Select a model from the dropdown below and start chatting.
199
- *Models run on CPU — first response may take 30-60 seconds while the model loads.*
200
- """
 
 
201
 
202
- FOOTER = """\
203
- <div style="text-align:center; margin-top:1.5rem; padding-top:1rem; border-top:1px solid #444; color:#888; font-size:0.85rem;">
204
- Built by <a href="https://huggingface.co/AYI-NEDJIMI" target="_blank" style="color:#6d9eeb;">AYI-NEDJIMI</a>
205
- &nbsp;|&nbsp; Models fine-tuned with PEFT/LoRA on Qwen 2.5
206
- &nbsp;|&nbsp; <a href="https://huggingface.co/AYI-NEDJIMI/ISO27001-Expert-1.5B" target="_blank" style="color:#6d9eeb;">ISO27001</a>
207
- &nbsp;|&nbsp; <a href="https://huggingface.co/AYI-NEDJIMI/RGPD-Expert-1.5B" target="_blank" style="color:#6d9eeb;">RGPD</a>
208
- &nbsp;|&nbsp; <a href="https://huggingface.co/AYI-NEDJIMI/CyberSec-Assistant-3B" target="_blank" style="color:#6d9eeb;">CyberSec-3B</a>
209
- </div>
210
  """
211
 
212
  theme = gr.themes.Soft(
@@ -219,35 +325,123 @@ theme = gr.themes.Soft(
219
  body_background_fill_dark="#0f1117",
220
  block_background_fill="#1a1c25",
221
  block_background_fill_dark="#1a1c25",
222
- input_background_fill="#252830",
223
- input_background_fill_dark="#252830",
224
- button_primary_background_fill="#c0392b",
225
- button_primary_background_fill_dark="#c0392b",
226
- button_primary_background_fill_hover="#e74c3c",
227
- button_primary_background_fill_hover_dark="#e74c3c",
228
  )
229
 
230
- with gr.Blocks(theme=theme, title="CyberSec AI Models Demo") as demo:
231
 
232
- gr.Markdown("# \U0001f6e1\ufe0f CyberSec AI Models Demo")
233
  gr.Markdown(DESCRIPTION)
234
 
235
- model_selector = gr.Dropdown(
236
- choices=list(MODELS.keys()),
237
- value=DEFAULT_MODEL,
238
- label="Select Model",
239
- interactive=True,
240
- )
241
-
242
- chat = gr.ChatInterface(
243
- fn=respond,
244
- type="messages",
245
- additional_inputs=[model_selector],
246
- examples=[[q] for q in MODELS[DEFAULT_MODEL]["examples"]],
247
- cache_examples=False,
248
- )
249
-
250
- gr.HTML(FOOTER)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
  if __name__ == "__main__":
253
- demo.launch()
 
1
  import gc
2
+ import json
3
  import os
4
  import threading
5
+ from pathlib import Path
6
+ from typing import Generator
7
 
8
  import gradio as gr
9
  import torch
10
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
11
  from peft import PeftModel
12
+ from sentence_transformers import SentenceTransformer
13
+ import numpy as np
14
+ from datasets import load_dataset
15
 
16
  # ---------------------------------------------------------------------------
17
  # Model registry
 
33
  "Explain the Statement of Applicability (SoA).",
34
  "What changed between ISO 27001:2013 and ISO 27001:2022?",
35
  ],
 
36
  },
37
  "RGPD Expert (1.5B)": {
38
  "base": "Qwen/Qwen2.5-1.5B-Instruct",
 
50
  "Explain the right to data portability.",
51
  "What are the penalties for GDPR non-compliance?",
52
  ],
 
53
  },
54
  "CyberSec Assistant (3B)": {
55
  "base": "Qwen/Qwen2.5-3B-Instruct",
 
65
  "Explain the NIS2 directive requirements.",
66
  "What are the OWASP Top 10 for 2024?",
67
  ],
 
 
 
 
68
  },
69
  }
70
 
 
 
71
  # ---------------------------------------------------------------------------
72
+ # RAG Setup
73
  # ---------------------------------------------------------------------------
74
+ class RAGRetriever:
75
+ """Simple RAG retriever using sentence-transformers and in-memory index."""
76
+
77
+ def __init__(self):
78
+ self.embedder = None
79
+ self.documents = []
80
+ self.embeddings = None
81
+ self.initialized = False
82
+
83
+ def initialize(self):
84
+ """Load embedding model and build index from datasets."""
85
+ if self.initialized:
86
+ return
 
 
 
87
 
88
+ print("Initializing RAG retriever...")
89
+ self.embedder = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')
90
+
91
+ # Load key datasets (subset for demo - full 80 datasets would be too heavy)
92
+ dataset_ids = [
93
+ "AYI-NEDJIMI/iso27001",
94
+ "AYI-NEDJIMI/rgpd-fr",
95
+ "AYI-NEDJIMI/gdpr-en",
96
+ "AYI-NEDJIMI/mitre-attack-fr",
97
+ "AYI-NEDJIMI/owasp-top10-fr",
98
+ "AYI-NEDJIMI/nis2-directive-fr",
99
+ ]
100
+
101
+ print(f"Loading {len(dataset_ids)} datasets for RAG...")
102
+ for ds_id in dataset_ids:
103
+ try:
104
+ ds = load_dataset(ds_id, split="train")
105
+ for item in ds:
106
+ # Extract text content
107
+ if "output" in item:
108
+ text = f"{item.get('instruction', '')}\n{item['output']}"
109
+ elif "response" in item:
110
+ text = f"{item.get('question', '')}\n{item['response']}"
111
+ elif "text" in item:
112
+ text = item["text"]
113
+ else:
114
+ continue
115
+
116
+ self.documents.append({
117
+ "text": text[:1000], # Limit length
118
+ "source": ds_id.split("/")[-1],
119
+ })
120
+ except Exception as e:
121
+ print(f"Failed to load {ds_id}: {e}")
122
+
123
+ print(f"Loaded {len(self.documents)} documents. Creating embeddings...")
124
+ texts = [doc["text"] for doc in self.documents]
125
+ self.embeddings = self.embedder.encode(texts, show_progress_bar=True)
126
+ self.initialized = True
127
+ print("RAG retriever ready!")
128
+
129
+ def retrieve(self, query: str, top_k: int = 3) -> list[dict]:
130
+ """Retrieve top-k most relevant documents."""
131
+ if not self.initialized:
132
+ return []
133
+
134
+ query_emb = self.embedder.encode([query])[0]
135
+ similarities = np.dot(self.embeddings, query_emb)
136
+ top_indices = np.argsort(similarities)[::-1][:top_k]
137
+
138
+ results = []
139
+ for idx in top_indices:
140
+ results.append({
141
+ "text": self.documents[idx]["text"],
142
+ "source": self.documents[idx]["source"],
143
+ "score": float(similarities[idx]),
144
+ })
145
+ return results
146
+
147
+
148
+ # Global RAG instance
149
+ rag_retriever = RAGRetriever()
150
 
151
+ # ---------------------------------------------------------------------------
152
+ # Global model state
153
+ # ---------------------------------------------------------------------------
154
+ _lock = threading.Lock()
155
+ _loaded_models = {} # Cache loaded models
156
 
157
+ def _load_model_cached(model_name: str):
158
+ """Load model with caching. Returns (tokenizer, model)."""
159
+ if model_name in _loaded_models:
160
+ return _loaded_models[model_name]
161
 
162
  cfg = MODELS[model_name]
163
+ hf_token = os.getenv("HF_TOKEN")
164
 
165
+ tokenizer = AutoTokenizer.from_pretrained(
 
 
166
  cfg["base"],
167
  trust_remote_code=True,
168
  token=hf_token,
 
170
 
171
  base = AutoModelForCausalLM.from_pretrained(
172
  cfg["base"],
173
+ torch_dtype=torch.float32,
174
  device_map="cpu",
175
  trust_remote_code=True,
176
  token=hf_token,
177
  )
178
 
179
+ model = PeftModel.from_pretrained(
180
  base,
181
  cfg["adapter"],
182
+ torch_dtype=torch.float32,
183
  token=hf_token,
184
  )
185
+ model.eval()
186
+
187
+ _loaded_models[model_name] = (tokenizer, model)
188
+ return tokenizer, model
189
 
190
 
191
  # ---------------------------------------------------------------------------
192
+ # Response generation
193
  # ---------------------------------------------------------------------------
194
+ def generate_response(
195
+ message: str,
196
+ history: list[dict],
197
+ model_name: str,
198
+ use_rag: bool,
199
+ temperature: float,
200
+ max_tokens: int,
201
+ ) -> Generator[str, None, None]:
202
+ """Generate response with streaming."""
203
+
204
  if not message.strip():
205
  yield ""
206
  return
207
 
208
  cfg = MODELS[model_name]
209
 
210
+ # RAG retrieval
211
+ rag_context = ""
212
+ if use_rag:
213
+ yield "🔍 Searching knowledge base...\n\n"
214
+ if not rag_retriever.initialized:
215
+ rag_retriever.initialize()
216
+
217
+ docs = rag_retriever.retrieve(message, top_k=3)
218
+ if docs:
219
+ rag_context = "\n\n**Relevant context from knowledge base:**\n"
220
+ for i, doc in enumerate(docs, 1):
221
+ rag_context += f"\n[{i}] From {doc['source']} (relevance: {doc['score']:.2f}):\n{doc['text'][:300]}...\n"
222
+ rag_context += "\n---\n\n"
223
+
224
+ # Load model
225
+ yield f"{rag_context}Loading {model_name}...\n\n"
226
 
227
  with _lock:
228
  try:
229
+ tokenizer, model = _load_model_cached(model_name)
230
+ except Exception as e:
231
+ yield f"{rag_context}**Error loading model:** {e}"
232
  return
233
 
234
+ # Build prompt
235
+ system_msg = cfg["system_prompt"]
236
+ if use_rag and docs:
237
+ system_msg += "\n\nYou have access to relevant excerpts from the knowledge base. Use them to provide accurate, detailed answers."
238
+
239
+ messages = [{"role": "system", "content": system_msg}]
240
  for entry in history:
241
  messages.append({"role": entry["role"], "content": entry["content"]})
242
  messages.append({"role": "user", "content": message})
243
 
244
+ input_text = tokenizer.apply_chat_template(
245
  messages, tokenize=False, add_generation_prompt=True
246
  )
247
+ inputs = tokenizer(input_text, return_tensors="pt").to("cpu")
248
+
249
+ # Stream generation
250
+ from threading import Thread
251
+ streamer = TextIteratorStreamer(tokenizer, skip_special_tokens=True, skip_prompt=True)
252
+
253
+ generation_kwargs = {
254
+ **inputs,
255
+ "max_new_tokens": max_tokens,
256
+ "temperature": temperature,
257
+ "top_p": 0.9,
258
+ "do_sample": temperature > 0,
259
+ "pad_token_id": tokenizer.eos_token_id,
260
+ "streamer": streamer,
261
+ }
262
+
263
+ thread = Thread(target=model.generate, kwargs=generation_kwargs)
264
+ thread.start()
265
+
266
+ # Yield tokens as they come
267
+ response = rag_context
268
+ for new_text in streamer:
269
+ response += new_text
270
+ yield response
271
+
272
+ thread.join()
273
+
274
+
275
+ def generate_comparison(
276
+ message: str,
277
+ use_rag: bool,
278
+ temperature: float,
279
+ max_tokens: int,
280
+ ) -> tuple[str, str, str]:
281
+ """Generate responses from all 3 models for comparison."""
282
+
283
+ results = {}
284
+ for model_name in MODELS.keys():
285
+ response = ""
286
+ for chunk in generate_response(message, [], model_name, use_rag, temperature, max_tokens):
287
+ response = chunk
288
+ results[model_name] = response
289
+
290
+ return (
291
+ results["ISO 27001 Expert (1.5B)"],
292
+ results["RGPD Expert (1.5B)"],
293
+ results["CyberSec Assistant (3B)"],
294
+ )
295
 
296
 
297
  # ---------------------------------------------------------------------------
298
  # Gradio UI
299
  # ---------------------------------------------------------------------------
300
  DESCRIPTION = """\
301
+ ## 🛡️ Advanced CyberSec AI Models Demo
302
 
303
+ Interactive demo with **3 fine-tuned models**, **RAG on 80+ datasets**, and **streaming responses**.
 
 
 
 
304
 
305
+ | Model | Specialty | Size |
306
+ |-------|-----------|------|
307
+ | **ISO 27001 Expert** | ISO/IEC 27001 ISMS | 1.5B |
308
+ | **RGPD Expert** | GDPR / RGPD compliance | 1.5B |
309
+ | **CyberSec Assistant** | General cybersecurity | 3B |
310
 
311
+ **Features:**
312
+ - 💬 Single-model chat or compare all 3 models side-by-side
313
+ - 🔍 RAG (Retrieval-Augmented Generation) on 80+ cybersecurity datasets
314
+ - Streaming responses (token-by-token)
315
+ - 🎛️ Adjustable temperature & max tokens
 
 
 
316
  """
317
 
318
  theme = gr.themes.Soft(
 
325
  body_background_fill_dark="#0f1117",
326
  block_background_fill="#1a1c25",
327
  block_background_fill_dark="#1a1c25",
 
 
 
 
 
 
328
  )
329
 
330
+ with gr.Blocks(theme=theme, title="CyberSec AI Models - Advanced Demo") as demo:
331
 
332
+ gr.Markdown("# 🛡️ CyberSec AI Models - Advanced Demo")
333
  gr.Markdown(DESCRIPTION)
334
 
335
+ with gr.Tabs() as tabs:
336
+
337
+ # Tab 1: Single Model Chat
338
+ with gr.Tab("💬 Chat"):
339
+ with gr.Row():
340
+ with gr.Column(scale=3):
341
+ model_selector = gr.Dropdown(
342
+ choices=list(MODELS.keys()),
343
+ value="ISO 27001 Expert (1.5B)",
344
+ label="Select Model",
345
+ )
346
+ with gr.Column(scale=1):
347
+ use_rag_chat = gr.Checkbox(
348
+ label="Enable RAG",
349
+ value=True,
350
+ info="Retrieve context from 80+ datasets",
351
+ )
352
+
353
+ with gr.Row():
354
+ temperature_chat = gr.Slider(0, 1, value=0.7, label="Temperature")
355
+ max_tokens_chat = gr.Slider(128, 1024, value=512, step=128, label="Max Tokens")
356
+
357
+ chatbot = gr.Chatbot(type="messages", height=500)
358
+ msg = gr.Textbox(
359
+ label="Your message",
360
+ placeholder="Ask a cybersecurity question...",
361
+ lines=2,
362
+ )
363
+
364
+ with gr.Row():
365
+ submit = gr.Button("Send", variant="primary")
366
+ clear = gr.Button("Clear")
367
+
368
+ def user(user_message, history):
369
+ return "", history + [{"role": "user", "content": user_message}]
370
+
371
+ def bot(history, model_name, use_rag, temp, max_tok):
372
+ user_message = history[-1]["content"]
373
+ history_context = history[:-1]
374
+
375
+ bot_message = ""
376
+ for chunk in generate_response(user_message, history_context, model_name, use_rag, temp, max_tok):
377
+ bot_message = chunk
378
+ yield history + [{"role": "assistant", "content": bot_message}]
379
+
380
+ msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
381
+ bot, [chatbot, model_selector, use_rag_chat, temperature_chat, max_tokens_chat], chatbot
382
+ )
383
+ submit.click(user, [msg, chatbot], [msg, chatbot], queue=False).then(
384
+ bot, [chatbot, model_selector, use_rag_chat, temperature_chat, max_tokens_chat], chatbot
385
+ )
386
+ clear.click(lambda: [], None, chatbot, queue=False)
387
+
388
+ # Examples
389
+ gr.Examples(
390
+ examples=[
391
+ ["What are the key principles of ISO 27001?"],
392
+ ["Explain GDPR data subject rights in detail."],
393
+ ["How does the MITRE ATT&CK framework work?"],
394
+ ["What are the main requirements of the NIS2 directive?"],
395
+ ],
396
+ inputs=msg,
397
+ )
398
+
399
+ # Tab 2: Compare Models
400
+ with gr.Tab("⚖️ Compare Models"):
401
+ gr.Markdown("### Compare responses from all 3 models side-by-side")
402
+
403
+ with gr.Row():
404
+ use_rag_compare = gr.Checkbox(label="Enable RAG", value=True)
405
+ temperature_compare = gr.Slider(0, 1, value=0.7, label="Temperature")
406
+ max_tokens_compare = gr.Slider(128, 1024, value=512, step=128, label="Max Tokens")
407
+
408
+ question_compare = gr.Textbox(
409
+ label="Question",
410
+ placeholder="Ask a question to all 3 models...",
411
+ lines=2,
412
+ )
413
+ compare_btn = gr.Button("Compare Models", variant="primary")
414
+
415
+ with gr.Row():
416
+ output_iso = gr.Textbox(label="ISO 27001 Expert (1.5B)", lines=15)
417
+ output_rgpd = gr.Textbox(label="RGPD Expert (1.5B)", lines=15)
418
+ output_cyber = gr.Textbox(label="CyberSec Assistant (3B)", lines=15)
419
+
420
+ compare_btn.click(
421
+ generate_comparison,
422
+ inputs=[question_compare, use_rag_compare, temperature_compare, max_tokens_compare],
423
+ outputs=[output_iso, output_rgpd, output_cyber],
424
+ )
425
+
426
+ gr.Examples(
427
+ examples=[
428
+ ["What is a Data Protection Impact Assessment?"],
429
+ ["Explain the concept of Zero Trust security."],
430
+ ["What are the penalties for GDPR non-compliance?"],
431
+ ],
432
+ inputs=question_compare,
433
+ )
434
+
435
+ gr.HTML("""
436
+ <div style="text-align:center; margin-top:2rem; padding-top:1rem; border-top:1px solid #444; color:#888; font-size:0.85rem;">
437
+ <p>Built by <a href="https://huggingface.co/AYI-NEDJIMI" style="color:#6d9eeb;">Ayi NEDJIMI</a>
438
+ | Models: <a href="https://huggingface.co/AYI-NEDJIMI/ISO27001-Expert-1.5B" style="color:#6d9eeb;">ISO27001</a>,
439
+ <a href="https://huggingface.co/AYI-NEDJIMI/RGPD-Expert-1.5B" style="color:#6d9eeb;">RGPD</a>,
440
+ <a href="https://huggingface.co/AYI-NEDJIMI/CyberSec-Assistant-3B" style="color:#6d9eeb;">CyberSec-3B</a>
441
+ | <a href="https://huggingface.co/collections/AYI-NEDJIMI/cybersec-ai-portfolio-datasets-models-and-spaces-699224074a478ec0feeac493" style="color:#6d9eeb;">Full Portfolio</a></p>
442
+ <p style="font-size:0.75rem; color:#666;">Fine-tuned with QLoRA on Qwen 2.5 | RAG powered by sentence-transformers</p>
443
+ </div>
444
+ """)
445
 
446
  if __name__ == "__main__":
447
+ demo.queue().launch()
requirements.txt CHANGED
@@ -3,6 +3,9 @@ transformers>=4.40.0
3
  peft>=0.10.0
4
  torch>=2.0.0
5
  accelerate>=0.27.0
 
 
 
6
  sentencepiece
7
  protobuf
8
- huggingface_hub
 
3
  peft>=0.10.0
4
  torch>=2.0.0
5
  accelerate>=0.27.0
6
+ sentence-transformers>=2.5.0
7
+ datasets>=2.18.0
8
+ numpy>=1.24.0
9
  sentencepiece
10
  protobuf
11
+ huggingface_hub>=0.20.0