hmusman2804045-max commited on
Commit
babcc93
·
1 Parent(s): 77d0745

Phase 9 prep: Add modal_app.py, update CI/CD for Modal.com, clean up HF Spaces files, update README

Browse files
Files changed (5) hide show
  1. .github/workflows/deploy.yml +14 -12
  2. README.md +9 -18
  3. gradio_app.py +0 -268
  4. modal_app.py +65 -0
  5. requirements.txt +1 -1
.github/workflows/deploy.yml CHANGED
@@ -1,4 +1,4 @@
1
- name: Deploy to HuggingFace Spaces
2
 
3
  on:
4
  push:
@@ -7,21 +7,23 @@ on:
7
 
8
  jobs:
9
  deploy:
10
- name: Push to HuggingFace Spaces (Gradio)
11
  runs-on: ubuntu-latest
12
 
13
  steps:
14
- - name: Checkout GitHub Repository
15
  uses: actions/checkout@v4
 
 
 
16
  with:
17
- fetch-depth: 0
18
- lfs: true
 
 
19
 
20
- - name: Push to HuggingFace Gradio Space
21
  env:
22
- HF_TOKEN: ${{ secrets.HF_TOKEN }}
23
- run: |
24
- git config --global user.email "ci@github-actions.com"
25
- git config --global user.name "GitHub Actions"
26
- git remote add hfspace https://hmusman2804045-max:$HF_TOKEN@huggingface.co/spaces/hmusman2804045-max/urdu-sentiment-engine
27
- git push hfspace main --force
 
1
+ name: Deploy to Modal.com
2
 
3
  on:
4
  push:
 
7
 
8
  jobs:
9
  deploy:
10
+ name: Deploy FastAPI to Modal.com
11
  runs-on: ubuntu-latest
12
 
13
  steps:
14
+ - name: Checkout Repository
15
  uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
  with:
20
+ python-version: "3.10"
21
+
22
+ - name: Install Modal CLI
23
+ run: pip install modal
24
 
25
+ - name: Deploy to Modal
26
  env:
27
+ MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
28
+ MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
29
+ run: modal deploy modal_app.py
 
 
 
README.md CHANGED
@@ -1,22 +1,9 @@
1
- ---
2
- title: Urdu Sentiment and Emotion Engine
3
- emoji: 🇵🇰
4
- colorFrom: violet
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 4.44.0
8
- app_file: gradio_app.py
9
- pinned: true
10
- license: apache-2.0
11
- short_description: XLM-RoBERTa fine-tuned for Urdu & Roman Urdu sentiment + emotion
12
- ---
13
-
14
  # Urdu Sentiment and Emotion Analysis Engine
15
 
16
  Welcome to the Urdu Sentiment and Emotion Analysis Engine project! This repository contains the code for a multilingual NLP system that classifies sentiment (Positive, Negative, Neutral) and emotion (Joy, Anger, Fear, Sadness) from Urdu, Roman Urdu, and mixed-language text using a fine-tuned XLM-RoBERTa transformer.
17
 
18
- ## Current Progress: Phase 7 (Frontend UI & Dashboard Complete)
19
- The project has successfully completed Phases 1 through 7. The AI models are fully trained and integrated into a production-ready **FastAPI** web server with Uvicorn. The frontend features a dark-mode Glassmorphism dashboard with an interactive 3D WebGL Three.js particle wave background, floating ambient glowing orbs, real-time cursor spotlight, Chart.js analytics, and automated live tweet feed streaming.
20
 
21
  ### Repository Structure
22
  - `app.py`: FastAPI Web Server exposing all REST API routes (`/analyze`, `/analytics`, `/detect-language`, `/live-feed`, `/health`).
@@ -28,6 +15,7 @@ The project has successfully completed Phases 1 through 7. The AI models are ful
28
  - `js/bg3d.js`: Three.js 3D WebGL particle wave and floating embers motion engine.
29
  - `js/main.js`: Interactivity handlers, GSAP timelines, Chart.js charts, and FastAPI endpoint fetch calls.
30
  - `upload_to_hub.py`: Automated model upload script for Hugging Face Hub integration.
 
31
  - `requirements.txt`: Environment dependencies required for training and the FastAPI server.
32
  - `Dockerfile`: Container configuration configured to run FastAPI with Uvicorn on port 7860.
33
  - `test_models.py`: Utility script to run interactive CLI inference without starting the server.
@@ -75,6 +63,9 @@ The server will boot up and listen on `http://127.0.0.1:5000`.
75
  | `GET` | `/health` | Health check endpoint for Docker / deployment monitors |
76
  | `GET` | `/docs` | Interactive Swagger API documentation UI |
77
 
78
- ### Next Steps (Phase 8 & 9)
79
- - **Phase 8**: Push trained models to Hugging Face Hub (`upload_to_hub.py`) & update `predictor.py`.
80
- - **Phase 9**: Containerize with Docker and deploy live to Hugging Face Spaces.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Urdu Sentiment and Emotion Analysis Engine
2
 
3
  Welcome to the Urdu Sentiment and Emotion Analysis Engine project! This repository contains the code for a multilingual NLP system that classifies sentiment (Positive, Negative, Neutral) and emotion (Joy, Anger, Fear, Sadness) from Urdu, Roman Urdu, and mixed-language text using a fine-tuned XLM-RoBERTa transformer.
4
 
5
+ ## Current Progress: Phase 9 (Modal.com Deployment In Progress)
6
+ The project has successfully completed Phases 1 through 8. The AI models are fully trained, uploaded to Hugging Face Hub (`usman-ai-dev/urdu-sentiment-xlmr` & `usman-ai-dev/urdu-emotion-xlmr`), and integrated into a production-ready **FastAPI** web server with Uvicorn. The frontend features a dark-mode Glassmorphism dashboard with an interactive 3D WebGL Three.js particle wave background, floating ambient glowing orbs, real-time cursor spotlight, Chart.js analytics, and automated live tweet feed streaming. Phase 9 deploys the full stack to **Modal.com** with a custom domain (`urdu-ai.hmuhammadusman.com`).
7
 
8
  ### Repository Structure
9
  - `app.py`: FastAPI Web Server exposing all REST API routes (`/analyze`, `/analytics`, `/detect-language`, `/live-feed`, `/health`).
 
15
  - `js/bg3d.js`: Three.js 3D WebGL particle wave and floating embers motion engine.
16
  - `js/main.js`: Interactivity handlers, GSAP timelines, Chart.js charts, and FastAPI endpoint fetch calls.
17
  - `upload_to_hub.py`: Automated model upload script for Hugging Face Hub integration.
18
+ - `modal_app.py`: Modal.com deployment entrypoint — wraps FastAPI app for serverless cloud deployment.
19
  - `requirements.txt`: Environment dependencies required for training and the FastAPI server.
20
  - `Dockerfile`: Container configuration configured to run FastAPI with Uvicorn on port 7860.
21
  - `test_models.py`: Utility script to run interactive CLI inference without starting the server.
 
63
  | `GET` | `/health` | Health check endpoint for Docker / deployment monitors |
64
  | `GET` | `/docs` | Interactive Swagger API documentation UI |
65
 
66
+ ### Deployment (Phase 9 Modal.com)
67
+ - **Phase 8**: Models pushed to Hugging Face Hub. `predictor.py` updated to load from Hub.
68
+ - **Phase 9**: Deploy full FastAPI stack to **Modal.com** (free $30/month credit tier).
69
+ - Run: `modal deploy modal_app.py`
70
+ - Custom domain: `urdu-ai.hmuhammadusman.com`
71
+ - GitHub Actions auto-deploys on every push to `main`.
gradio_app.py DELETED
@@ -1,268 +0,0 @@
1
- import gradio as gr
2
- from predictor import SentimentEmotionPredictor
3
-
4
- # ── Load models once at startup ──────────────────────────────────────────────
5
- print("Initialising Urdu Sentiment & Emotion Engine…")
6
- engine = SentimentEmotionPredictor()
7
- print("Engine ready.")
8
-
9
- # ── Emoji / colour maps ───────────────────────────────────────────────────────
10
- SENTIMENT_EMOJI = {"Positive": "😊", "Negative": "😞", "Neutral": "😐"}
11
- EMOTION_EMOJI = {"Joy": "🎉", "Anger": "😡", "Fear": "😨", "Sadness": "😢"}
12
-
13
- SENTIMENT_COLOR = {
14
- "Positive": "#22c55e",
15
- "Negative": "#ef4444",
16
- "Neutral": "#facc15",
17
- }
18
- EMOTION_COLOR = {
19
- "Joy": "#f59e0b",
20
- "Anger": "#ef4444",
21
- "Fear": "#8b5cf6",
22
- "Sadness": "#3b82f6",
23
- }
24
-
25
- # ── Example inputs ─────────────────────────────────────────────────────────────
26
- EXAMPLES = [
27
- ["آج کا دن بہت اچھا ہے، بہت خوشی ہوئی"],
28
- ["mujhe bohat gussa aa raha hai is cheez par"],
29
- ["یہ صورتحال بہت خطرناک اور ڈراؤنی ہے"],
30
- ["Aaj mera dil bohat udaas hai, kuch bhi acha nahi lag raha"],
31
- ["بالکل ٹھیک ہے، کوئی خاص بات نہیں"],
32
- ["Yeh sab dekh kar dil khush ho gaya, wah wah!"],
33
- ]
34
-
35
-
36
- def build_attention_html(attention_list):
37
- if not attention_list:
38
- return "<p style='color:#9ca3af;font-size:0.85rem'>No attention data.</p>"
39
- max_score = max(a["score"] for a in attention_list) or 1.0
40
- html = "<div style='display:flex;flex-wrap:wrap;gap:6px;padding:8px 0;'>"
41
- for item in attention_list:
42
- intensity = item["score"] / max_score
43
- alpha = 0.15 + intensity * 0.75
44
- font_w = 400 + int(intensity * 300)
45
- html += (
46
- f"<span style='background:rgba(139,92,246,{alpha:.2f});color:#e9d5ff;"
47
- f"padding:3px 8px;border-radius:12px;font-size:0.9rem;"
48
- f"font-weight:{font_w};border:1px solid rgba(139,92,246,0.3);'>"
49
- f"{item['word']}</span>"
50
- )
51
- html += "</div>"
52
- return html
53
-
54
-
55
- def build_bar(label, score, color):
56
- pct = round(score * 100, 1)
57
- return (
58
- f"<div style='margin-bottom:8px;'>"
59
- f"<div style='display:flex;justify-content:space-between;font-size:0.82rem;"
60
- f"color:#d1d5db;margin-bottom:3px;'><span>{label}</span><span>{pct}%</span></div>"
61
- f"<div style='background:#1f2937;border-radius:999px;height:8px;overflow:hidden;'>"
62
- f"<div style='width:{pct}%;background:{color};height:100%;border-radius:999px;"
63
- f"transition:width 0.6s ease;'></div></div></div>"
64
- )
65
-
66
-
67
- def analyse(text):
68
- if not text or not text.strip():
69
- return (
70
- "<p style='color:#ef4444'>Please enter some Urdu or Roman Urdu text.</p>",
71
- "", "", "",
72
- )
73
-
74
- result = engine.predict(text)
75
-
76
- if "error" in result:
77
- return (f"<p style='color:#ef4444'>{result['error']}</p>", "", "", "")
78
-
79
- sentiment = result["sentiment"]
80
- emotion = result["emotion"]
81
- s_scores = result["sentiment_scores"]
82
- e_scores = result["emotion_scores"]
83
- attention = result["attention"]
84
-
85
- s_emoji = SENTIMENT_EMOJI.get(sentiment, "")
86
- e_emoji = EMOTION_EMOJI.get(emotion, "")
87
- s_color = SENTIMENT_COLOR.get(sentiment, "#6b7280")
88
- e_color = EMOTION_COLOR.get(emotion, "#6b7280")
89
-
90
- result_html = f"""
91
- <div style='background:linear-gradient(135deg,#1e1b4b 0%,#111827 100%);
92
- border:1px solid rgba(139,92,246,0.35);border-radius:16px;padding:20px 24px;
93
- font-family:Inter,sans-serif;'>
94
- <div style='display:flex;gap:16px;flex-wrap:wrap;'>
95
- <div style='flex:1;min-width:140px;background:rgba(0,0,0,0.3);
96
- border:2px solid {s_color};border-radius:12px;padding:14px 18px;text-align:center;'>
97
- <div style='font-size:2rem;'>{s_emoji}</div>
98
- <div style='font-size:0.72rem;letter-spacing:0.1em;color:#9ca3af;margin:4px 0 2px;'>SENTIMENT</div>
99
- <div style='font-size:1.25rem;font-weight:700;color:{s_color};'>{sentiment}</div>
100
- </div>
101
- <div style='flex:1;min-width:140px;background:rgba(0,0,0,0.3);
102
- border:2px solid {e_color};border-radius:12px;padding:14px 18px;text-align:center;'>
103
- <div style='font-size:2rem;'>{e_emoji}</div>
104
- <div style='font-size:0.72rem;letter-spacing:0.1em;color:#9ca3af;margin:4px 0 2px;'>EMOTION</div>
105
- <div style='font-size:1.25rem;font-weight:700;color:{e_color};'>{emotion}</div>
106
- </div>
107
- </div>
108
- </div>
109
- """
110
-
111
- s_bars_html = "<div style='padding:4px 0;'>"
112
- for lbl, sc in s_scores.items():
113
- s_bars_html += build_bar(lbl, sc, SENTIMENT_COLOR.get(lbl, "#6b7280"))
114
- s_bars_html += "</div>"
115
-
116
- e_bars_html = "<div style='padding:4px 0;'>"
117
- for lbl, sc in e_scores.items():
118
- e_bars_html += build_bar(lbl, sc, EMOTION_COLOR.get(lbl, "#6b7280"))
119
- e_bars_html += "</div>"
120
-
121
- attn_html = build_attention_html(attention)
122
-
123
- return result_html, s_bars_html, e_bars_html, attn_html
124
-
125
-
126
- CSS = """
127
- body, .gradio-container {
128
- background: #0f0c29 !important;
129
- font-family: 'Inter', sans-serif !important;
130
- }
131
- #header-banner {
132
- background: linear-gradient(135deg,#1a0533 0%,#0f172a 50%,#0c1445 100%);
133
- border-bottom: 1px solid rgba(139,92,246,0.3);
134
- padding: 28px 24px 18px;
135
- text-align: center;
136
- border-radius: 16px 16px 0 0;
137
- margin-bottom: 4px;
138
- }
139
- #header-banner h1 {
140
- font-size: clamp(1.4rem, 4vw, 2rem);
141
- font-weight: 800;
142
- background: linear-gradient(90deg, #a78bfa, #60a5fa, #34d399);
143
- -webkit-background-clip: text;
144
- -webkit-text-fill-color: transparent;
145
- margin: 0 0 6px;
146
- letter-spacing: -0.02em;
147
- }
148
- #header-banner p { color: #94a3b8; font-size: 0.9rem; margin: 0; }
149
- #input-box textarea {
150
- background: #1e1b4b !important;
151
- border: 1.5px solid rgba(139,92,246,0.4) !important;
152
- border-radius: 12px !important;
153
- color: #e2e8f0 !important;
154
- font-size: 1rem !important;
155
- line-height: 1.6 !important;
156
- padding: 14px !important;
157
- }
158
- #input-box textarea:focus {
159
- border-color: #a78bfa !important;
160
- box-shadow: 0 0 0 3px rgba(167,139,250,0.15) !important;
161
- }
162
- #analyse-btn {
163
- background: linear-gradient(135deg,#7c3aed,#4f46e5) !important;
164
- border: none !important;
165
- border-radius: 10px !important;
166
- font-weight: 700 !important;
167
- font-size: 1rem !important;
168
- color: #fff !important;
169
- padding: 10px 0 !important;
170
- transition: opacity 0.2s !important;
171
- }
172
- #analyse-btn:hover { opacity: 0.88 !important; }
173
- #clear-btn {
174
- background: rgba(31,41,55,0.8) !important;
175
- border: 1px solid rgba(139,92,246,0.3) !important;
176
- border-radius: 10px !important;
177
- color: #9ca3af !important;
178
- }
179
- .section-label {
180
- font-size: 0.72rem;
181
- letter-spacing: 0.12em;
182
- color: #7c3aed;
183
- font-weight: 700;
184
- text-transform: uppercase;
185
- margin-bottom: 6px;
186
- }
187
- .output-panel {
188
- background: rgba(17,24,39,0.85) !important;
189
- border: 1px solid rgba(139,92,246,0.25) !important;
190
- border-radius: 14px !important;
191
- padding: 16px !important;
192
- }
193
- #footer {
194
- text-align: center;
195
- color: #4b5563;
196
- font-size: 0.78rem;
197
- margin-top: 16px;
198
- padding: 12px 0 4px;
199
- border-top: 1px solid rgba(139,92,246,0.15);
200
- }
201
- """
202
-
203
- with gr.Blocks(
204
- theme=gr.themes.Base(
205
- primary_hue="violet",
206
- neutral_hue="slate",
207
- font=gr.themes.GoogleFont("Inter"),
208
- ),
209
- css=CSS,
210
- title="Urdu Sentiment & Emotion Engine",
211
- ) as demo:
212
-
213
- gr.HTML("""
214
- <div id="header-banner">
215
- <h1>🇵🇰 Urdu Sentiment &amp; Emotion Analysis Engine</h1>
216
- <p>XLM-RoBERTa fine-tuned on Urdu · Roman Urdu · Mixed language text</p>
217
- </div>
218
- """)
219
-
220
- with gr.Row():
221
- with gr.Column(scale=5):
222
- gr.HTML("<p class='section-label'>✍️ Enter Text</p>")
223
- text_input = gr.Textbox(
224
- placeholder="اردو یا Roman Urdu میں لکھیں…\nYa Roman Urdu mein likhein…",
225
- lines=5,
226
- max_lines=10,
227
- show_label=False,
228
- elem_id="input-box",
229
- )
230
- with gr.Row():
231
- analyse_btn = gr.Button("🔍 Analyse", variant="primary", elem_id="analyse-btn")
232
- clear_btn = gr.Button("✕ Clear", variant="secondary", elem_id="clear-btn")
233
-
234
- gr.HTML("<p class='section-label' style='margin-top:18px;'>💡 Try an Example</p>")
235
- gr.Examples(examples=EXAMPLES, inputs=text_input, label="")
236
-
237
- with gr.Column(scale=5):
238
- gr.HTML("<p class='section-label'>🎯 Prediction</p>")
239
- result_out = gr.HTML(elem_classes=["output-panel"])
240
-
241
- with gr.Row():
242
- with gr.Column():
243
- gr.HTML("<p class='section-label' style='margin-top:14px;'>📊 Sentiment Confidence</p>")
244
- sent_bars = gr.HTML(elem_classes=["output-panel"])
245
- with gr.Column():
246
- gr.HTML("<p class='section-label' style='margin-top:14px;'>📊 Emotion Confidence</p>")
247
- emot_bars = gr.HTML(elem_classes=["output-panel"])
248
-
249
- gr.HTML("<p class='section-label' style='margin-top:14px;'>🔦 Word Attention Highlights</p>")
250
- attn_out = gr.HTML(elem_classes=["output-panel"])
251
-
252
- gr.HTML("""
253
- <div id="footer">
254
- Powered by <strong>XLM-RoBERTa</strong> · Fine-tuned by <strong>Muhammad Usman</strong> ·
255
- <a href="https://github.com/hmusman2804045-max/Urdu-Sentiment-and-Emotion-Analysis-Engine"
256
- style="color:#7c3aed;" target="_blank">GitHub ↗</a>
257
- </div>
258
- """)
259
-
260
- analyse_btn.click(fn=analyse, inputs=text_input,
261
- outputs=[result_out, sent_bars, emot_bars, attn_out])
262
- text_input.submit(fn=analyse, inputs=text_input,
263
- outputs=[result_out, sent_bars, emot_bars, attn_out])
264
- clear_btn.click(fn=lambda: ("", "", "", ""), inputs=None,
265
- outputs=[result_out, sent_bars, emot_bars, attn_out])
266
-
267
- if __name__ == "__main__":
268
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
modal_app.py ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ modal_app.py — Phase 9: Modal.com Deployment Entrypoint
3
+ ---------------------------------------------------------
4
+ Wraps the existing FastAPI app (app.py) for serverless deployment
5
+ on Modal.com with custom domain support.
6
+
7
+ Deploy command:
8
+ modal deploy modal_app.py
9
+
10
+ Local test command:
11
+ modal serve modal_app.py
12
+ """
13
+
14
+ import modal
15
+
16
+ # ── Docker image with all dependencies ────────────────────────────────────────
17
+ image = (
18
+ modal.Image.debian_slim(python_version="3.10")
19
+ .pip_install(
20
+ "fastapi==0.110.0",
21
+ "uvicorn==0.27.0",
22
+ "pydantic==2.6.0",
23
+ "torch==2.2.0",
24
+ "transformers==4.40.0",
25
+ "huggingface_hub==0.22.0",
26
+ "numpy==1.26.0",
27
+ "pandas==2.2.0",
28
+ "scikit-learn==1.4.0",
29
+ "accelerate==0.29.0",
30
+ )
31
+ )
32
+
33
+ # ── Modal app definition ───────────────────────────────────────────────────────
34
+ app = modal.App("urdu-sentiment-engine", image=image)
35
+
36
+ # ── Mount local project files into the container ──────────────────────────────
37
+ project_mount = modal.Mount.from_local_dir(
38
+ ".",
39
+ remote_path="/app",
40
+ # Exclude large/unnecessary directories
41
+ condition=lambda path: not any(
42
+ part in path for part in [
43
+ "models", "urdu_env", "__pycache__", ".git",
44
+ "data", "logs", "results", "training", "evaluation",
45
+ ".github", "kaggle_upload.zip",
46
+ ]
47
+ ),
48
+ )
49
+
50
+ @app.function(
51
+ mounts=[project_mount],
52
+ # Keeps one container warm to avoid cold starts on the first request
53
+ min_containers=1,
54
+ # Give enough CPU/memory for the two XLM-RoBERTa models (~4GB RAM)
55
+ memory=4096,
56
+ cpu=2.0,
57
+ # Models download from HF Hub on first cold-start then are cached
58
+ timeout=300,
59
+ )
60
+ @modal.asgi_app()
61
+ def fastapi_app():
62
+ import sys
63
+ sys.path.insert(0, "/app")
64
+ from app import app as _app # Import the existing FastAPI app
65
+ return _app
requirements.txt CHANGED
@@ -8,4 +8,4 @@ numpy==1.26.0
8
  pandas==2.2.0
9
  scikit-learn==1.4.0
10
  accelerate==0.29.0
11
- gradio==4.44.0
 
8
  pandas==2.2.0
9
  scikit-learn==1.4.0
10
  accelerate==0.29.0
11
+