Rayugacodes commited on
Commit
e019ca1
Β·
verified Β·
1 Parent(s): 644149b

Full-screen UI + OpenEnv API tab (reset/step/state/stop)

Browse files
Files changed (1) hide show
  1. app.py +266 -215
app.py CHANGED
@@ -1,10 +1,11 @@
1
  """
2
- KernelX β€” Interactive Kernel Scheduler Simulation
3
  AI-Powered Linux Scheduling with eBPF + SmolLM2-360M
4
  """
5
 
6
  import json
7
  import random
 
8
  import numpy as np
9
  import gradio as gr
10
  import plotly.graph_objects as go
@@ -20,6 +21,7 @@ IDX_CTX_SWITCHES = 8
20
  IDX_EXEC_NS = 4
21
 
22
  COLORS = {"baseline": "#6b7280", "heuristic": "#f59e0b", "ai": "#06b6d4"}
 
23
 
24
  def format_state(features):
25
  return " | ".join(
@@ -88,7 +90,6 @@ def load_data():
88
  from huggingface_hub import hf_hub_download
89
  path = hf_hub_download(repo_id="Rayugacodes/kernelx-training-data", filename="test.jsonl", repo_type="dataset")
90
  DATA = [json.loads(l) for l in open(path) if l.strip()][:5000]
91
- print(f"Loaded {len(DATA)} transitions")
92
  except Exception:
93
  DATA = []
94
  for i in range(2000):
@@ -99,31 +100,83 @@ def load_data():
99
  load_data()
100
 
101
  # ---------------------------------------------------------------------------
102
- # Simulation engine
103
  # ---------------------------------------------------------------------------
104
 
105
- def run_full_simulation(n_steps):
106
- n = int(n_steps)
107
- recs = random.sample(DATA, min(n, len(DATA)))
108
-
109
- results = {k: {"rewards": [], "latencies": [], "actions": [], "cum_rewards": []} for k in ["baseline", "heuristic", "ai"]}
110
- prevs = {"baseline": 0., "heuristic": 0., "ai": 0.}
111
- fns = {"baseline": baseline_action, "heuristic": heuristic_action, "ai": ai_action}
112
-
113
- for rec in recs:
114
- s, ns_raw = rec["state"], rec["next_state"]
115
- for k, fn in fns.items():
116
- a = fn(s)
117
- ns = simulate_effect(s, ns_raw, a)
118
- r = compute_reward(s, ns, a, prevs[k])
119
- results[k]["rewards"].append(r)
120
- results[k]["latencies"].append(ns[IDX_WAIT_US])
121
- results[k]["actions"].append(a)
122
- cum = (results[k]["cum_rewards"][-1] if results[k]["cum_rewards"] else 0) + r
123
- results[k]["cum_rewards"].append(cum)
124
- prevs[k] = a
125
-
126
- return results, recs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
  # ---------------------------------------------------------------------------
129
  # Charts
@@ -131,20 +184,18 @@ def run_full_simulation(n_steps):
131
 
132
  CHART_LAYOUT = dict(
133
  template="plotly_dark",
134
- paper_bgcolor="#0f172a",
135
  plot_bgcolor="#1e293b",
136
- font=dict(color="#e2e8f0", family="JetBrains Mono, monospace"),
137
  margin=dict(l=50, r=20, t=50, b=40),
138
  legend=dict(bgcolor="rgba(0,0,0,0.3)", bordercolor="#334155"),
139
  )
140
 
141
- LABELS = {"baseline": "Linux CFS (Default)", "heuristic": "Heuristic Rules", "ai": "AI Strategist (SmolLM2)"}
142
-
143
  def make_cumulative_chart(results):
144
  fig = go.Figure()
145
  for k in ["baseline", "heuristic", "ai"]:
146
  fig.add_trace(go.Scatter(y=results[k]["cum_rewards"], name=LABELS[k], line=dict(color=COLORS[k], width=2.5)))
147
- fig.update_layout(**CHART_LAYOUT, title="Cumulative Reward Over Time", xaxis_title="Step", yaxis_title="Cumulative Reward", height=400)
148
  fig.add_hline(y=0, line_dash="dash", line_color="#475569", opacity=0.5)
149
  return fig
150
 
@@ -156,78 +207,82 @@ def make_latency_chart(results):
156
  if len(lat) >= window:
157
  smooth = np.convolve(lat, np.ones(window)/window, mode="valid")
158
  fig.add_trace(go.Scatter(y=smooth, name=LABELS[k], line=dict(color=COLORS[k], width=2.5)))
159
- fig.update_layout(**CHART_LAYOUT, title="Rolling Average Latency (lower = better)", xaxis_title="Step", yaxis_title="Wait Time (us)", height=400)
160
  return fig
161
 
162
  def make_action_chart(results):
163
  fig = make_subplots(rows=1, cols=3, subplot_titles=[LABELS[k] for k in ["baseline", "heuristic", "ai"]])
164
  for i, k in enumerate(["baseline", "heuristic", "ai"], 1):
165
  fig.add_trace(go.Histogram(x=results[k]["actions"], nbinsx=40, marker_color=COLORS[k], opacity=0.8, showlegend=False), row=1, col=i)
166
- fig.update_layout(**CHART_LAYOUT, title="Action Distribution", height=300)
167
  fig.update_xaxes(range=[-1.1, 1.1])
168
  return fig
169
 
170
  def make_summary_bars(results):
171
- labels_list = [LABELS[k] for k in ["baseline", "heuristic", "ai"]]
172
- colors_list = [COLORS[k] for k in ["baseline", "heuristic", "ai"]]
173
-
174
- fig = make_subplots(rows=1, cols=3, subplot_titles=["Mean Reward (higher=better)", "Avg Latency (lower=better)", "Positive Reward %"])
175
- rewards = [np.mean(results[k]["rewards"]) for k in ["baseline", "heuristic", "ai"]]
176
- lats = [np.mean(results[k]["latencies"]) for k in ["baseline", "heuristic", "ai"]]
177
- pos = [sum(1 for r in results[k]["rewards"] if r > 0)/len(results[k]["rewards"])*100 for k in ["baseline", "heuristic", "ai"]]
 
 
 
 
178
 
179
- fig.add_trace(go.Bar(x=labels_list, y=rewards, marker_color=colors_list, showlegend=False, text=[f"{v:.2f}" for v in rewards], textposition="outside"), row=1, col=1)
180
- fig.add_trace(go.Bar(x=labels_list, y=lats, marker_color=colors_list, showlegend=False, text=[f"{v:.1f}" for v in lats], textposition="outside"), row=1, col=2)
181
- fig.add_trace(go.Bar(x=labels_list, y=pos, marker_color=colors_list, showlegend=False, text=[f"{v:.0f}%" for v in pos], textposition="outside"), row=1, col=3)
182
 
183
- fig.update_layout(**CHART_LAYOUT, height=350)
184
- return fig
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
  # ---------------------------------------------------------------------------
187
  # Gradio handlers
188
  # ---------------------------------------------------------------------------
189
 
190
  def simulate(n_steps):
191
- results, recs = run_full_simulation(n_steps)
192
-
193
- # Metrics
194
  base_r, heur_r, ai_r = np.mean(results["baseline"]["rewards"]), np.mean(results["heuristic"]["rewards"]), np.mean(results["ai"]["rewards"])
195
  base_l, ai_l = np.mean(results["baseline"]["latencies"]), np.mean(results["ai"]["latencies"])
196
  lat_imp = ((base_l - ai_l) / base_l * 100) if base_l > 0 else 0
197
  reward_imp = ((ai_r - base_r) / abs(base_r) * 100) if base_r != 0 else 0
198
 
199
- summary_md = f"""
200
- ### Results ({int(n_steps)} steps on real kernel telemetry)
201
-
202
  | | Linux CFS | Heuristic | **AI Strategist** |
203
  |---|---|---|---|
204
- | Mean Reward | {base_r:.4f} | {heur_r:.4f} | **{ai_r:.4f}** |
205
- | Avg Latency | {base_l:.1f}us | {np.mean(results['heuristic']['latencies']):.1f}us | **{ai_l:.1f}us** |
206
- | Latency Reduction | β€” | {((base_l - np.mean(results['heuristic']['latencies'])) / base_l * 100):.1f}% | **{lat_imp:.1f}%** |
207
- | Reward vs Baseline | β€” | {((heur_r - base_r) / abs(base_r) * 100):+.1f}% | **{reward_imp:+.1f}%** |
208
  """
209
-
210
- return (
211
- summary_md,
212
- make_cumulative_chart(results),
213
- make_latency_chart(results),
214
- make_action_chart(results),
215
- make_summary_bars(results),
216
- )
217
 
218
 
219
  def explore_state(idx):
220
  rec = DATA[int(idx) % len(DATA)]
221
  s, ns_raw = rec["state"], rec["next_state"]
222
-
223
  a_b, a_h, a_ai = baseline_action(s), heuristic_action(s), ai_action(s)
224
- ns_b = simulate_effect(s, ns_raw, a_b)
225
- ns_h = simulate_effect(s, ns_raw, a_h)
226
- ns_ai = simulate_effect(s, ns_raw, a_ai)
227
- r_b = compute_reward(s, ns_b, a_b)
228
- r_h = compute_reward(s, ns_h, a_h)
229
- r_ai = compute_reward(s, ns_ai, a_ai)
230
-
231
  wait = s[IDX_WAIT_US]
232
  lat_imp = ((ns_b[IDX_WAIT_US] - ns_ai[IDX_WAIT_US]) / ns_b[IDX_WAIT_US] * 100) if ns_b[IDX_WAIT_US] > 0 else 0
233
 
@@ -238,226 +293,222 @@ def explore_state(idx):
238
  elif a > 0.05: return "slight demote"
239
  return "HOLD"
240
 
241
- if wait > 50: reason = f"Very high latency ({wait:.0f}us) β€” aggressive priority boost to reduce scheduling delay."
242
- elif wait > 15: reason = f"Elevated latency ({wait:.0f}us) β€” boosting priority to improve responsiveness."
243
  elif wait < 3: reason = f"Very low latency ({wait:.0f}us) β€” system healthy, minimal adjustment."
244
- else: reason = f"Normal latency ({wait:.0f}us) β€” near-neutral action to maintain stability."
245
 
246
  md = f"""
247
- ### Transition #{int(idx)}
248
  **PID** {rec['pid']} | **CPU** {rec['cpu']} | **Wait** {wait:.0f}us | **CSW** {s[IDX_CTX_SWITCHES]:.0f}
249
 
250
- `{format_state(s)}`
251
-
252
  | Strategy | Action | Decision | Result Latency | Reward |
253
  |---|---|---|---|---|
254
  | Linux CFS | {a_b:+.4f} | {meaning(a_b)} | {ns_b[IDX_WAIT_US]:.1f}us | {r_b:+.4f} |
255
  | Heuristic | {a_h:+.4f} | {meaning(a_h)} | {ns_h[IDX_WAIT_US]:.1f}us | {r_h:+.4f} |
256
  | **AI Strategist** | **{a_ai:+.4f}** | **{meaning(a_ai)}** | **{ns_ai[IDX_WAIT_US]:.1f}us** | **{r_ai:+.4f}** |
257
 
258
- **AI reduced latency by {lat_imp:.1f}%** vs Linux default.
259
-
260
- > **AI Reasoning:** {reason}
261
  """
262
-
263
- # Mini chart: action comparison
264
  fig = go.Figure()
265
- fig.add_trace(go.Bar(x=["Linux CFS", "Heuristic", "AI"], y=[a_b, a_h, a_ai],
266
  marker_color=[COLORS["baseline"], COLORS["heuristic"], COLORS["ai"]],
267
  text=[f"{a_b:+.2f}", f"{a_h:+.2f}", f"{a_ai:+.2f}"], textposition="outside"))
268
- fig.update_layout(**CHART_LAYOUT, title="Action Comparison", yaxis_title="Action Value", height=280,
269
- yaxis_range=[-1.1, 0.5])
270
  fig.add_hline(y=0, line_dash="dash", line_color="#475569")
271
-
272
  return md, fig
273
 
274
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  # ---------------------------------------------------------------------------
276
  # App
277
  # ---------------------------------------------------------------------------
278
 
279
  CSS = """
280
- .gradio-container { max-width: 1400px !important; }
 
 
 
281
  .dark { background-color: #0f172a !important; }
282
- h1 { color: #06b6d4 !important; font-family: 'JetBrains Mono', monospace !important; }
283
  h2, h3 { color: #e2e8f0 !important; }
284
- .metric-box { background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 16px; text-align: center; }
285
- .metric-value { font-size: 2em; font-weight: bold; color: #06b6d4; }
286
- .metric-label { color: #94a3b8; font-size: 0.9em; }
287
  """
288
 
289
  with gr.Blocks(title="KernelX β€” AI Kernel Scheduler", css=CSS, theme=gr.themes.Base(primary_hue="cyan", neutral_hue="slate")) as app:
290
 
291
- # Header
292
  gr.Markdown("""
293
- # KernelX
294
- ### AI-Powered Linux Kernel Scheduler | eBPF + SmolLM2-360M | 44ms Inference
295
-
296
- Real-time scheduling optimization using reinforcement learning on live kernel telemetry.
297
- 534K transitions collected via eBPF sentinel. Model trained with SFT + GRPO.
 
298
  """)
299
 
300
- # Tab 1: Live Simulation
301
- with gr.Tab("Simulation", id="sim"):
302
- gr.Markdown("#### Compare AI Strategist vs Linux Default vs Heuristic on real kernel data")
303
  with gr.Row():
304
- n_slider = gr.Slider(50, 2000, value=500, step=50, label="Simulation Steps", scale=3)
305
  run_btn = gr.Button("Run Simulation", variant="primary", scale=1, size="lg")
306
-
307
  summary = gr.Markdown()
308
-
309
- with gr.Row():
310
  cumulative_plot = gr.Plot(label="Cumulative Reward")
311
- latency_plot = gr.Plot(label="Latency Comparison")
312
-
 
 
 
 
 
 
313
  with gr.Row():
314
- action_plot = gr.Plot(label="Action Distribution")
315
-
316
- summary_bars = gr.Plot(label="Performance Summary")
 
 
 
 
 
317
 
318
- run_btn.click(
319
- fn=simulate, inputs=[n_slider],
320
- outputs=[summary, cumulative_plot, latency_plot, action_plot, summary_bars]
321
- )
322
 
323
- # Tab 2: State Explorer
324
- with gr.Tab("State Explorer", id="explore"):
325
- gr.Markdown("#### Inspect individual kernel states and see how each strategy decides")
326
  with gr.Row():
327
- idx_slider = gr.Slider(0, min(len(DATA)-1, 4999), value=0, step=1, label="Transition Index", scale=3)
328
- explore_btn = gr.Button("Analyze", variant="primary", scale=1)
329
-
330
  with gr.Row():
331
- state_md = gr.Markdown()
332
- action_bar = gr.Plot(label="Action Comparison")
 
333
 
334
- explore_btn.click(fn=explore_state, inputs=[idx_slider], outputs=[state_md, action_bar])
 
 
 
335
 
336
- # Tab 3: RL Explanation
337
- with gr.Tab("How RL Improves", id="rl"):
338
  gr.Markdown("""
339
- ## Policy Iteration: How KernelX Gets Smarter
 
 
340
 
341
  ```
342
  COLLECT TRAIN DEPLOY
343
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
344
- β”‚ Run live β”‚ β”‚ SFT warm- β”‚ β”‚ Hot-swap β”‚
345
  β”‚ kernel β”‚ ────────> β”‚ start + β”‚ ───────> β”‚ GGUF model β”‚ ──┐
346
- β”‚ w/ policy β”‚ JSONL β”‚ GRPO RL β”‚ .gguf β”‚ in brain β”‚ β”‚
347
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
348
  ^ β”‚
349
- └───────────────── REPEAT with better policy β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
350
  ```
351
 
352
- ### Iteration Progression
353
-
354
- | Iteration | Policy | Behavior | Expected Improvement |
355
- |:---------:|--------|----------|---------------------|
356
- | **0** | Linux CFS Default | No AI intervention. Generic scheduler. | Baseline |
357
- | **1** | SFT Warm-Start | Learns from heuristic labels. Matches rules. | Match heuristic |
358
- | **2** | GRPO on Iter 1 | Sees ACTUAL outcomes of its actions. | +10-20% over heuristic |
359
- | **3+** | GRPO on Iter 2+ | Recursive self-improvement. | Diminishing returns |
360
-
361
- ### Why AI Beats the Default Scheduler
362
-
363
- The Linux **Completely Fair Scheduler (CFS)** is designed for *all possible workloads*.
364
- It has no knowledge of YOUR specific system's patterns.
365
-
366
- KernelX learns:
367
- - Which PIDs are latency-sensitive (and should be boosted)
368
- - When high context switches indicate CPU contention (and should be dampened)
369
- - How vruntime correlates with scheduling fairness for YOUR workload
370
- - Timing patterns that no hand-written heuristic captures
371
 
372
  ### Training Evidence
373
 
374
- | Metric | Before | After | Change |
375
- |--------|--------|-------|--------|
376
- | Training Loss | 2.05 | 0.28 | -86% |
377
- | Token Accuracy | 61% | 91% | +49% |
378
- | Format Compliance | 0% | 100% | β€” |
379
- | Inference Latency | β€” | 44ms | Sub-50ms target met |
380
- | Model Size | 1.4GB | 258MB | Q4_K_M quantization |
381
 
382
  ### Reward Function
383
 
384
- $$R_t = \\alpha \\cdot \\log(\\Delta_{exec} + 1) - \\beta \\cdot \\Delta_{wait} - \\gamma \\cdot |a_t - a_{t-1}|$$
385
 
386
- | Component | Weight | What it rewards |
387
- |-----------|--------|----------------|
388
- | Throughput | alpha=1.0 | CPU progress (more exec_runtime = good) |
389
- | Latency | beta=2.0 | Low wait time (penalizes increases) |
390
- | Stability | gamma=0.5 | Smooth actions (penalizes jitter) |
 
 
391
  """)
392
 
393
- # Tab 4: Architecture
394
- with gr.Tab("Architecture", id="arch"):
395
  gr.Markdown("""
396
- ## KernelX System Architecture
 
 
397
 
398
  ```
399
- β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
400
- β”‚ LINUX KERNEL SPACE β”‚
401
- β”‚ β”‚
402
- β”‚ sched_switch ──> eBPF Sentinel ──> 24D Feature Vector β”‚
403
- β”‚ β”‚ β”‚ β”‚
404
- β”‚ priority_actions map <── BPF Ring Buffer β”€β”€β”˜ β”‚
405
- β”‚ β”‚ β”‚ β”‚
406
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
407
- β”‚ β”‚
408
- β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€v──────────┐
409
- β”‚ β”‚ RUST BRIDGE β”‚
410
- β”‚ β”‚ β”‚
411
- β”‚ β”‚ Ring Buffer ──> SHM (/dev/shm/kernelx_state)
412
- β”‚ β”‚ β”‚ β”‚
413
- β”‚ β”‚ └──> trajectories.jsonl β”‚
414
- β”‚ β”‚ β”‚
415
- β”‚ β”‚ ZMQ Sub <── action weights β”‚
416
- β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
417
- β”‚ β”‚
418
- β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€v──────────┐
419
  β”‚ β”‚ PYTHON BRAIN β”‚
420
  β”‚ β”‚ (OpenEnv) β”‚
421
  β”‚ β”‚ β”‚
422
- β”‚ β”‚ SHM ──> 10D features ──> SmolLM2-360M β”‚
423
- β”‚ β”‚ β”‚ β”‚
424
- β”‚ β”‚ Action [-1, 1] <β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
425
- β”‚ β”‚ β”‚ β”‚
426
- β”‚ β”‚ └──> ZMQ Pub ──> Bridge β”‚
427
  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
428
- β”‚
429
- └──── Kernel applies scheduling nudge at next sched_switch
430
  ```
431
 
432
- ### Component Details
433
-
434
- | Component | Language | Role | Latency |
435
- |-----------|---------|------|---------|
436
- | eBPF Sentinel | C | Kernel telemetry extraction | <1us |
437
- | Rust Bridge | Rust | SHM sync + trajectory recording | <1ms |
438
- | Python Brain | Python | AI inference + OpenEnv server | 44ms |
439
- | SmolLM2-360M | GGUF | Scheduling decision model | 44ms |
440
- | Ratatui TUI | Rust | Real-time monitoring dashboard | 100ms refresh |
441
-
442
- ### Data Flow
443
-
444
- | Step | Data | Format | Size |
445
- |------|------|--------|------|
446
- | Kernel -> Bridge | 24D telemetry | BPF ring buffer | 208 bytes/event |
447
- | Bridge -> Brain | Active state | Shared memory | 376 bytes |
448
- | Bridge -> Disk | Transitions | JSONL | ~300 bytes/line |
449
- | Brain -> Bridge | Action | ZMQ string | ~50 bytes |
450
- | Brain -> Kernel | Priority weight | BPF map | 8 bytes |
451
  """)
452
 
453
- # Footer
454
  gr.Markdown("""
455
- ---
456
- [Model](https://huggingface.co/Rayugacodes/kernelx-strategist) |
457
- [Data](https://huggingface.co/datasets/Rayugacodes/kernelx-training-data) |
458
- [Colab](https://colab.research.google.com/github/pie-314/KernelX/blob/model-training-hugging-face-integration/KernelX_Training.ipynb) |
459
- [GitHub](https://github.com/pie-314/KernelX) |
460
- Built for Meta PyTorch OpenEnv Hackathon 2026
 
461
  """)
462
 
463
  app.launch(server_name="0.0.0.0", server_port=7860)
 
1
  """
2
+ KernelX β€” Interactive Kernel Scheduler Simulation + OpenEnv API
3
  AI-Powered Linux Scheduling with eBPF + SmolLM2-360M
4
  """
5
 
6
  import json
7
  import random
8
+ import uuid
9
  import numpy as np
10
  import gradio as gr
11
  import plotly.graph_objects as go
 
21
  IDX_EXEC_NS = 4
22
 
23
  COLORS = {"baseline": "#6b7280", "heuristic": "#f59e0b", "ai": "#06b6d4"}
24
+ LABELS = {"baseline": "Linux CFS (Default)", "heuristic": "Heuristic Rules", "ai": "AI Strategist (SmolLM2)"}
25
 
26
  def format_state(features):
27
  return " | ".join(
 
90
  from huggingface_hub import hf_hub_download
91
  path = hf_hub_download(repo_id="Rayugacodes/kernelx-training-data", filename="test.jsonl", repo_type="dataset")
92
  DATA = [json.loads(l) for l in open(path) if l.strip()][:5000]
 
93
  except Exception:
94
  DATA = []
95
  for i in range(2000):
 
100
  load_data()
101
 
102
  # ---------------------------------------------------------------------------
103
+ # OpenEnv Environment State (for API endpoints)
104
  # ---------------------------------------------------------------------------
105
 
106
+ class KernelXSimEnv:
107
+ """OpenEnv-compliant environment running in simulation mode."""
108
+
109
+ def __init__(self):
110
+ self.episode_id = str(uuid.uuid4())
111
+ self.step_count = 0
112
+ self.current_idx = 0
113
+ self.prev_action = 0.0
114
+ self.cumulative_reward = 0.0
115
+ self.running = False
116
+
117
+ def reset(self):
118
+ self.episode_id = str(uuid.uuid4())
119
+ self.step_count = 0
120
+ self.current_idx = random.randint(0, len(DATA) - 100)
121
+ self.prev_action = 0.0
122
+ self.cumulative_reward = 0.0
123
+ self.running = True
124
+ obs = DATA[self.current_idx]["state"]
125
+ return {
126
+ "observation": obs,
127
+ "features": dict(zip(FEATURE_NAMES, obs)),
128
+ "pid": DATA[self.current_idx]["pid"],
129
+ "episode_id": self.episode_id,
130
+ }
131
+
132
+ def step(self, action_value=None):
133
+ if not self.running:
134
+ return {"error": "Environment not started. Call /reset first."}
135
+
136
+ rec = DATA[min(self.current_idx + self.step_count, len(DATA) - 1)]
137
+ state = rec["state"]
138
+ next_state_raw = rec["next_state"]
139
+
140
+ if action_value is None:
141
+ action_value = ai_action(state)
142
+
143
+ action_value = max(-1.0, min(1.0, float(action_value)))
144
+ ns = simulate_effect(state, next_state_raw, action_value)
145
+ reward = compute_reward(state, ns, action_value, self.prev_action)
146
+
147
+ self.step_count += 1
148
+ self.prev_action = action_value
149
+ self.cumulative_reward += reward
150
+
151
+ return {
152
+ "observation": ns,
153
+ "features": dict(zip(FEATURE_NAMES, ns)),
154
+ "action_taken": action_value,
155
+ "reward": reward,
156
+ "cumulative_reward": self.cumulative_reward,
157
+ "step": self.step_count,
158
+ "done": self.step_count >= 100,
159
+ "pid": rec["pid"],
160
+ }
161
+
162
+ def state(self):
163
+ return {
164
+ "episode_id": self.episode_id,
165
+ "step_count": self.step_count,
166
+ "cumulative_reward": self.cumulative_reward,
167
+ "running": self.running,
168
+ }
169
+
170
+ def stop(self):
171
+ self.running = False
172
+ return {
173
+ "episode_id": self.episode_id,
174
+ "total_steps": self.step_count,
175
+ "final_reward": self.cumulative_reward,
176
+ "status": "stopped",
177
+ }
178
+
179
+ ENV = KernelXSimEnv()
180
 
181
  # ---------------------------------------------------------------------------
182
  # Charts
 
184
 
185
  CHART_LAYOUT = dict(
186
  template="plotly_dark",
187
+ paper_bgcolor="rgba(0,0,0,0)",
188
  plot_bgcolor="#1e293b",
189
+ font=dict(color="#e2e8f0", family="Inter, system-ui, sans-serif", size=12),
190
  margin=dict(l=50, r=20, t=50, b=40),
191
  legend=dict(bgcolor="rgba(0,0,0,0.3)", bordercolor="#334155"),
192
  )
193
 
 
 
194
  def make_cumulative_chart(results):
195
  fig = go.Figure()
196
  for k in ["baseline", "heuristic", "ai"]:
197
  fig.add_trace(go.Scatter(y=results[k]["cum_rewards"], name=LABELS[k], line=dict(color=COLORS[k], width=2.5)))
198
+ fig.update_layout(**CHART_LAYOUT, title="Cumulative Reward", xaxis_title="Step", yaxis_title="Reward", height=380)
199
  fig.add_hline(y=0, line_dash="dash", line_color="#475569", opacity=0.5)
200
  return fig
201
 
 
207
  if len(lat) >= window:
208
  smooth = np.convolve(lat, np.ones(window)/window, mode="valid")
209
  fig.add_trace(go.Scatter(y=smooth, name=LABELS[k], line=dict(color=COLORS[k], width=2.5)))
210
+ fig.update_layout(**CHART_LAYOUT, title="Rolling Avg Latency (lower = better)", xaxis_title="Step", yaxis_title="Wait (us)", height=380)
211
  return fig
212
 
213
  def make_action_chart(results):
214
  fig = make_subplots(rows=1, cols=3, subplot_titles=[LABELS[k] for k in ["baseline", "heuristic", "ai"]])
215
  for i, k in enumerate(["baseline", "heuristic", "ai"], 1):
216
  fig.add_trace(go.Histogram(x=results[k]["actions"], nbinsx=40, marker_color=COLORS[k], opacity=0.8, showlegend=False), row=1, col=i)
217
+ fig.update_layout(**CHART_LAYOUT, title="Action Distributions", height=280)
218
  fig.update_xaxes(range=[-1.1, 1.1])
219
  return fig
220
 
221
  def make_summary_bars(results):
222
+ names = [LABELS[k] for k in ["baseline", "heuristic", "ai"]]
223
+ cols = [COLORS[k] for k in ["baseline", "heuristic", "ai"]]
224
+ fig = make_subplots(rows=1, cols=3, subplot_titles=["Mean Reward", "Avg Latency (us)", "Positive %"])
225
+ r = [np.mean(results[k]["rewards"]) for k in ["baseline", "heuristic", "ai"]]
226
+ l = [np.mean(results[k]["latencies"]) for k in ["baseline", "heuristic", "ai"]]
227
+ p = [sum(1 for x in results[k]["rewards"] if x > 0)/len(results[k]["rewards"])*100 for k in ["baseline", "heuristic", "ai"]]
228
+ fig.add_trace(go.Bar(x=names, y=r, marker_color=cols, showlegend=False, text=[f"{v:.2f}" for v in r], textposition="outside"), row=1, col=1)
229
+ fig.add_trace(go.Bar(x=names, y=l, marker_color=cols, showlegend=False, text=[f"{v:.1f}" for v in l], textposition="outside"), row=1, col=2)
230
+ fig.add_trace(go.Bar(x=names, y=p, marker_color=cols, showlegend=False, text=[f"{v:.0f}%" for v in p], textposition="outside"), row=1, col=3)
231
+ fig.update_layout(**CHART_LAYOUT, height=320)
232
+ return fig
233
 
234
+ # ---------------------------------------------------------------------------
235
+ # Simulation engine
236
+ # ---------------------------------------------------------------------------
237
 
238
+ def run_full_simulation(n_steps):
239
+ n = int(n_steps)
240
+ recs = random.sample(DATA, min(n, len(DATA)))
241
+ results = {k: {"rewards": [], "latencies": [], "actions": [], "cum_rewards": []} for k in ["baseline", "heuristic", "ai"]}
242
+ prevs = {"baseline": 0., "heuristic": 0., "ai": 0.}
243
+ fns = {"baseline": baseline_action, "heuristic": heuristic_action, "ai": ai_action}
244
+ for rec in recs:
245
+ s, ns_raw = rec["state"], rec["next_state"]
246
+ for k, fn in fns.items():
247
+ a = fn(s)
248
+ ns = simulate_effect(s, ns_raw, a)
249
+ r = compute_reward(s, ns, a, prevs[k])
250
+ results[k]["rewards"].append(r)
251
+ results[k]["latencies"].append(ns[IDX_WAIT_US])
252
+ results[k]["actions"].append(a)
253
+ cum = (results[k]["cum_rewards"][-1] if results[k]["cum_rewards"] else 0) + r
254
+ results[k]["cum_rewards"].append(cum)
255
+ prevs[k] = a
256
+ return results
257
 
258
  # ---------------------------------------------------------------------------
259
  # Gradio handlers
260
  # ---------------------------------------------------------------------------
261
 
262
  def simulate(n_steps):
263
+ results = run_full_simulation(n_steps)
 
 
264
  base_r, heur_r, ai_r = np.mean(results["baseline"]["rewards"]), np.mean(results["heuristic"]["rewards"]), np.mean(results["ai"]["rewards"])
265
  base_l, ai_l = np.mean(results["baseline"]["latencies"]), np.mean(results["ai"]["latencies"])
266
  lat_imp = ((base_l - ai_l) / base_l * 100) if base_l > 0 else 0
267
  reward_imp = ((ai_r - base_r) / abs(base_r) * 100) if base_r != 0 else 0
268
 
269
+ md = f"""
 
 
270
  | | Linux CFS | Heuristic | **AI Strategist** |
271
  |---|---|---|---|
272
+ | **Mean Reward** | {base_r:.4f} | {heur_r:.4f} | **{ai_r:.4f}** |
273
+ | **Avg Latency** | {base_l:.1f}us | {np.mean(results['heuristic']['latencies']):.1f}us | **{ai_l:.1f}us** |
274
+ | **Latency Reduction** | β€” | {((base_l - np.mean(results['heuristic']['latencies'])) / base_l * 100):.1f}% | **{lat_imp:.1f}%** |
275
+ | **Reward vs Baseline** | β€” | {((heur_r - base_r) / abs(base_r) * 100):+.1f}% | **{reward_imp:+.1f}%** |
276
  """
277
+ return md, make_cumulative_chart(results), make_latency_chart(results), make_action_chart(results), make_summary_bars(results)
 
 
 
 
 
 
 
278
 
279
 
280
  def explore_state(idx):
281
  rec = DATA[int(idx) % len(DATA)]
282
  s, ns_raw = rec["state"], rec["next_state"]
 
283
  a_b, a_h, a_ai = baseline_action(s), heuristic_action(s), ai_action(s)
284
+ ns_b, ns_h, ns_ai = simulate_effect(s, ns_raw, a_b), simulate_effect(s, ns_raw, a_h), simulate_effect(s, ns_raw, a_ai)
285
+ r_b, r_h, r_ai = compute_reward(s, ns_b, a_b), compute_reward(s, ns_h, a_h), compute_reward(s, ns_ai, a_ai)
 
 
 
 
 
286
  wait = s[IDX_WAIT_US]
287
  lat_imp = ((ns_b[IDX_WAIT_US] - ns_ai[IDX_WAIT_US]) / ns_b[IDX_WAIT_US] * 100) if ns_b[IDX_WAIT_US] > 0 else 0
288
 
 
293
  elif a > 0.05: return "slight demote"
294
  return "HOLD"
295
 
296
+ if wait > 50: reason = f"Very high latency ({wait:.0f}us) β€” aggressive priority boost."
297
+ elif wait > 15: reason = f"Elevated latency ({wait:.0f}us) β€” boosting priority."
298
  elif wait < 3: reason = f"Very low latency ({wait:.0f}us) β€” system healthy, minimal adjustment."
299
+ else: reason = f"Normal latency ({wait:.0f}us) β€” near-neutral action."
300
 
301
  md = f"""
 
302
  **PID** {rec['pid']} | **CPU** {rec['cpu']} | **Wait** {wait:.0f}us | **CSW** {s[IDX_CTX_SWITCHES]:.0f}
303
 
 
 
304
  | Strategy | Action | Decision | Result Latency | Reward |
305
  |---|---|---|---|---|
306
  | Linux CFS | {a_b:+.4f} | {meaning(a_b)} | {ns_b[IDX_WAIT_US]:.1f}us | {r_b:+.4f} |
307
  | Heuristic | {a_h:+.4f} | {meaning(a_h)} | {ns_h[IDX_WAIT_US]:.1f}us | {r_h:+.4f} |
308
  | **AI Strategist** | **{a_ai:+.4f}** | **{meaning(a_ai)}** | **{ns_ai[IDX_WAIT_US]:.1f}us** | **{r_ai:+.4f}** |
309
 
310
+ **Latency reduction: {lat_imp:.1f}%** vs baseline | *{reason}*
 
 
311
  """
 
 
312
  fig = go.Figure()
313
+ fig.add_trace(go.Bar(x=["Linux CFS", "Heuristic", "AI Strategist"], y=[a_b, a_h, a_ai],
314
  marker_color=[COLORS["baseline"], COLORS["heuristic"], COLORS["ai"]],
315
  text=[f"{a_b:+.2f}", f"{a_h:+.2f}", f"{a_ai:+.2f}"], textposition="outside"))
316
+ fig.update_layout(**CHART_LAYOUT, title="Action Comparison", yaxis_title="Action", height=260, yaxis_range=[-1.1, 0.5])
 
317
  fig.add_hline(y=0, line_dash="dash", line_color="#475569")
 
318
  return md, fig
319
 
320
 
321
+ # OpenEnv API handlers for Gradio
322
+ def api_reset():
323
+ result = ENV.reset()
324
+ return json.dumps(result, indent=2)
325
+
326
+ def api_step(action_str):
327
+ try:
328
+ action = float(action_str) if action_str.strip() else None
329
+ except ValueError:
330
+ action = None
331
+ result = ENV.step(action)
332
+ return json.dumps(result, indent=2)
333
+
334
+ def api_state():
335
+ return json.dumps(ENV.state(), indent=2)
336
+
337
+ def api_stop():
338
+ return json.dumps(ENV.stop(), indent=2)
339
+
340
  # ---------------------------------------------------------------------------
341
  # App
342
  # ---------------------------------------------------------------------------
343
 
344
  CSS = """
345
+ .gradio-container { max-width: 100% !important; padding: 0 !important; }
346
+ .main { max-width: 100% !important; }
347
+ #component-0 { max-width: 100% !important; }
348
+ footer { display: none !important; }
349
  .dark { background-color: #0f172a !important; }
350
+ h1 { color: #06b6d4 !important; letter-spacing: -0.02em; }
351
  h2, h3 { color: #e2e8f0 !important; }
352
+ .tab-nav button { font-size: 1.05em !important; padding: 12px 24px !important; }
353
+ .tab-nav button.selected { border-bottom: 3px solid #06b6d4 !important; color: #06b6d4 !important; }
 
354
  """
355
 
356
  with gr.Blocks(title="KernelX β€” AI Kernel Scheduler", css=CSS, theme=gr.themes.Base(primary_hue="cyan", neutral_hue="slate")) as app:
357
 
 
358
  gr.Markdown("""
359
+ <div style="text-align:center; padding: 10px 0;">
360
+ <h1 style="font-size:2.5em; margin-bottom:0;">KernelX</h1>
361
+ <p style="color:#94a3b8; font-size:1.15em; margin-top:4px;">
362
+ AI-Powered Linux Kernel Scheduler &nbsp;|&nbsp; eBPF + SmolLM2-360M &nbsp;|&nbsp; 44ms Inference &nbsp;|&nbsp; 534K Real Transitions
363
+ </p>
364
+ </div>
365
  """)
366
 
367
+ # --- Tab 1: Simulation ---
368
+ with gr.Tab("Simulation"):
 
369
  with gr.Row():
370
+ n_slider = gr.Slider(50, 2000, value=500, step=50, label="Steps", scale=3)
371
  run_btn = gr.Button("Run Simulation", variant="primary", scale=1, size="lg")
 
372
  summary = gr.Markdown()
373
+ with gr.Row(equal_height=True):
 
374
  cumulative_plot = gr.Plot(label="Cumulative Reward")
375
+ latency_plot = gr.Plot(label="Latency")
376
+ with gr.Row(equal_height=True):
377
+ action_plot = gr.Plot(label="Actions")
378
+ summary_bars = gr.Plot(label="Summary")
379
+ run_btn.click(fn=simulate, inputs=[n_slider], outputs=[summary, cumulative_plot, latency_plot, action_plot, summary_bars])
380
+
381
+ # --- Tab 2: State Explorer ---
382
+ with gr.Tab("State Explorer"):
383
  with gr.Row():
384
+ idx_slider = gr.Slider(0, min(len(DATA)-1, 4999), value=0, step=1, label="Transition #", scale=3)
385
+ explore_btn = gr.Button("Analyze", variant="primary", scale=1)
386
+ with gr.Row():
387
+ with gr.Column(scale=2):
388
+ state_md = gr.Markdown()
389
+ with gr.Column(scale=1):
390
+ action_bar = gr.Plot(label="Actions")
391
+ explore_btn.click(fn=explore_state, inputs=[idx_slider], outputs=[state_md, action_bar])
392
 
393
+ # --- Tab 3: OpenEnv API ---
394
+ with gr.Tab("OpenEnv API"):
395
+ gr.Markdown("""
396
+ ### OpenEnv-Compliant Environment API
397
 
398
+ KernelX implements the standard `reset()` β†’ `step(action)` β†’ `state` β†’ `stop()` interface.
399
+ Use these buttons to interact with the environment programmatically.
400
+ """)
401
  with gr.Row():
402
+ reset_btn = gr.Button("reset()", variant="primary")
403
+ step_input = gr.Textbox(label="Action [-1.0 to 1.0]", placeholder="Leave blank for AI auto-action", scale=2)
404
+ step_btn = gr.Button("step(action)", variant="primary")
405
  with gr.Row():
406
+ state_btn = gr.Button("state()")
407
+ stop_btn = gr.Button("stop()", variant="stop")
408
+ api_output = gr.Code(label="Response (JSON)", language="json", lines=15)
409
 
410
+ reset_btn.click(fn=api_reset, outputs=[api_output])
411
+ step_btn.click(fn=api_step, inputs=[step_input], outputs=[api_output])
412
+ state_btn.click(fn=api_state, outputs=[api_output])
413
+ stop_btn.click(fn=api_stop, outputs=[api_output])
414
 
415
+ # --- Tab 4: How RL Improves ---
416
+ with gr.Tab("How RL Improves"):
417
  gr.Markdown("""
418
+ <div style="max-width:900px; margin: 0 auto;">
419
+
420
+ ## Policy Iteration Loop
421
 
422
  ```
423
  COLLECT TRAIN DEPLOY
424
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
425
+ β”‚ Run live β”‚ JSONL β”‚ SFT warm- β”‚ .gguf β”‚ Hot-swap β”‚
426
  β”‚ kernel β”‚ ────────> β”‚ start + β”‚ ───────> β”‚ GGUF model β”‚ ──┐
427
+ β”‚ w/ policy β”‚ β”‚ GRPO RL β”‚ β”‚ in brain β”‚ β”‚
428
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
429
  ^ β”‚
430
+ └───────────────── REPEAT with improved policy β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
431
  ```
432
 
433
+ | Iter | Policy | Improvement |
434
+ |:----:|--------|-------------|
435
+ | 0 | Linux CFS Default | Baseline (no AI) |
436
+ | 1 | SFT Warm-Start | Matches heuristic rules |
437
+ | 2 | GRPO on Iter 1 | Discovers patterns humans missed |
438
+ | 3+ | GRPO on Iter 2+ | Recursive self-improvement |
 
 
 
 
 
 
 
 
 
 
 
 
 
439
 
440
  ### Training Evidence
441
 
442
+ | Metric | Before | After |
443
+ |--------|--------|-------|
444
+ | Loss | 2.05 | 0.28 |
445
+ | Accuracy | 61% | 91% |
446
+ | Compliance | 0% | 100% |
447
+ | Inference | β€” | 44ms |
448
+ | Size | 1.4GB | 258MB |
449
 
450
  ### Reward Function
451
 
452
+ **R = Ξ±Β·log(Ξ”exec + 1) βˆ’ Ξ²Β·Ξ”wait βˆ’ Ξ³Β·|a βˆ’ a_prev|**
453
 
454
+ | Component | Weight | Signal |
455
+ |-----------|--------|--------|
456
+ | Throughput | Ξ±=1.0 | CPU progress |
457
+ | Latency | Ξ²=2.0 | Wait time penalty |
458
+ | Stability | Ξ³=0.5 | Jitter penalty |
459
+
460
+ </div>
461
  """)
462
 
463
+ # --- Tab 5: Architecture ---
464
+ with gr.Tab("Architecture"):
465
  gr.Markdown("""
466
+ <div style="max-width:900px; margin: 0 auto;">
467
+
468
+ ## System Architecture
469
 
470
  ```
471
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ KERNEL SPACE ───────────────────────┐
472
+ β”‚ β”‚
473
+ β”‚ sched_switch ──> eBPF Sentinel ──> 24D Feature Vector β”‚
474
+ β”‚ ↑ β”‚ β”‚
475
+ β”‚ priority_actions ←── BPF Ring Buffer β”€β”€β”€β”€β”€β”˜ β”‚
476
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
477
+ β”‚ β”Œβ”€β”€β”€β”€β”€v──────────────┐
478
+ β”‚ β”‚ RUST BRIDGE β”‚
479
+ β”‚ β”‚ Ring Buffer β†’ SHM β”‚
480
+ β”‚ β”‚ Ring Buffer β†’ JSONLβ”‚
481
+ β”‚ β”‚ ZMQ ← actions β”‚
482
+ β”‚ β””β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
483
+ β”‚ β”Œβ”€β”€β”€β”€β”€v──────────────┐
 
 
 
 
 
 
 
484
  β”‚ β”‚ PYTHON BRAIN β”‚
485
  β”‚ β”‚ (OpenEnv) β”‚
486
  β”‚ β”‚ β”‚
487
+ β”‚ β”‚ SHM β†’ 10D β†’ LLM β”‚
488
+ β”‚ β”‚ Action [-1, 1] β”‚
489
+ β”‚ β”‚ β†’ ZMQ β†’ Bridge β”‚
 
 
490
  β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
491
+ └── Kernel applies nudge at next sched_switch
 
492
  ```
493
 
494
+ | Component | Language | Latency |
495
+ |-----------|---------|---------|
496
+ | eBPF Sentinel | C | <1ΞΌs |
497
+ | Rust Bridge | Rust | <1ms |
498
+ | SmolLM2-360M | GGUF | 44ms |
499
+ | TUI Dashboard | Rust | 100ms |
500
+
501
+ </div>
 
 
 
 
 
 
 
 
 
 
 
502
  """)
503
 
 
504
  gr.Markdown("""
505
+ <div style="text-align:center; padding:10px; color:#64748b; font-size:0.9em;">
506
+ <a href="https://huggingface.co/Rayugacodes/kernelx-strategist">Model</a> Β·
507
+ <a href="https://huggingface.co/datasets/Rayugacodes/kernelx-training-data">Data</a> Β·
508
+ <a href="https://colab.research.google.com/github/pie-314/KernelX/blob/model-training-hugging-face-integration/KernelX_Training.ipynb">Colab</a> Β·
509
+ <a href="https://github.com/pie-314/KernelX">GitHub</a> Β·
510
+ Meta PyTorch OpenEnv Hackathon 2026
511
+ </div>
512
  """)
513
 
514
  app.launch(server_name="0.0.0.0", server_port=7860)