Dishaaa25 commited on
Commit
0ae9515
·
1 Parent(s): fae9841

Fix Space inference loading and switch evidence plots to SVG

Browse files
.gitignore CHANGED
@@ -223,3 +223,10 @@ __marimo__/
223
 
224
  # local codebase maps
225
  CODEBASE_STRUCTURE_MAIN_V5.md
 
 
 
 
 
 
 
 
223
 
224
  # local codebase maps
225
  CODEBASE_STRUCTURE_MAIN_V5.md
226
+
227
+ # recovered local-only artifacts
228
+ artifacts/events.jsonl
229
+ artifacts/reward_curve.csv
230
+ artifacts/run_summary.json
231
+ artifacts/test_plot.png
232
+ artifacts/*.png
BLOG.md ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ADAPT: training a model to debug algorithm solutions instead of guessing once
2
+
3
+ ADAPT is an OpenEnv environment for reinforcement learning on algorithmic problem solving. The core idea is simple: instead of rewarding a model only for a perfect first answer, we make it solve a programming task, inspect verifier feedback, and repair its code over up to three attempts.
4
+
5
+ That makes the environment closer to real software work. The agent sees a problem statement, input format, constraints, and visible examples. It writes Python code, gets hidden-test feedback, and is rewarded for improving hidden correctness and eventually meeting an efficiency target.
6
+
7
+ ## What we built
8
+
9
+ - an OpenEnv-compatible environment with `reset`, `step`, and `state`
10
+ - a verifier that executes submitted Python safely and scores hidden correctness, visible correctness, and efficiency
11
+ - a reward-aware curriculum that shifts toward the most educational problem families
12
+ - a GRPO training pipeline built with Unsloth + Hugging Face TRL
13
+
14
+ ## Why this environment is interesting
15
+
16
+ Most code-generation tasks only ask whether a model can get the answer immediately. ADAPT focuses on a harder and more realistic behavior: can the model recover from failure, use feedback, and converge toward a correct solution?
17
+
18
+ The environment includes `20` DSA problem families across easy, medium, and hard tiers, hidden evaluation tests, efficiency-aware rewards, and a curriculum that adapts based on what seems to teach the agent the most.
19
+
20
+ ## Real training run
21
+
22
+ We trained `Qwen/Qwen2.5-3B-Instruct` with the overnight preset:
23
+
24
+ - Run ID: `15940d1d-7d8c-4253-8810-2ea934bedee4`
25
+ - Optimizer steps: `950`
26
+ - Training episodes logged: `7,600`
27
+ - Wall-clock time: `8.96 hours`
28
+ - Uploaded model revision: `6c957e7c6bdb25ff086775fb8692570aee4501c9`
29
+
30
+ From the recovered training logs:
31
+
32
+ - average reward improved from `0.4441` in the first `500` episodes to `0.5951` in the last `500`
33
+ - average hidden pass rate improved from `0.4625` to `0.6488`
34
+ - completion rate improved from `43.6%` to `59.6%`
35
+
36
+ Late in training, the model was solving many easy and medium tasks reliably and was noticeably stronger on hard tasks as well:
37
+
38
+ | Difficulty | Avg reward | Avg hidden pass rate | Completion rate |
39
+ | --- | ---: | ---: | ---: |
40
+ | Easy | 0.8390 | 0.8477 | 84.38% |
41
+ | Medium | 0.7892 | 0.8425 | 78.85% |
42
+ | Hard | 0.5182 | 0.5759 | 51.92% |
43
+
44
+ ## Evidence
45
+
46
+ ![Reward curve](artifacts/reward_curve.svg)
47
+
48
+ ![Pass rate by difficulty](artifacts/pass_rate_by_difficulty.svg)
49
+
50
+ ![Family productivity](artifacts/family_productivity.svg)
51
+
52
+ ## Links
53
+
54
+ - Environment Space: [Dishaaa25/meta-rl-dsa-solver](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver)
55
+ - Live app: [Dishaaa25-meta-rl-dsa-solver.hf.space](https://Dishaaa25-meta-rl-dsa-solver.hf.space)
56
+ - Training notebook: [Qwen2.5_(3B)-GRPO.ipynb](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/Qwen2.5_%283B%29-GRPO.ipynb)
57
+ - Trained model: [Dishaaa25/adapt-dsa-tutor-model](https://huggingface.co/Dishaaa25/adapt-dsa-tutor-model)
README.md CHANGED
@@ -15,6 +15,17 @@ tags:
15
 
16
  LLMs are getting better at one-shot code generation, but they still struggle with the thing real engineers do all day: read feedback, debug, and repair. ADAPT closes that gap by turning algorithm practice into a self-repair RL environment where the model must improve over multiple attempts instead of guessing once.
17
 
 
 
 
 
 
 
 
 
 
 
 
18
  ## Why ADAPT exists
19
 
20
  Most code-generation benchmarks test whether a model can land the answer immediately. They do not test whether the model can recover from partial failure, use examples productively, or adapt as the task distribution changes.
@@ -187,17 +198,70 @@ That makes ADAPT more than a static benchmark. The environment actively searches
187
 
188
  ## Results
189
 
190
- [INSERT: reward curve plot]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
- [INSERT: baseline vs trained table]
193
 
194
- Recommended artifacts to include here:
195
 
196
- - reward curve from `training/reward_curve.csv`
197
- - `reward_curve.png`
198
- - `pass_rate_by_difficulty.png`
199
- - `family_productivity.png`
200
- - one before/after repair example from baseline vs trained evaluation
201
 
202
  ## How to run
203
 
@@ -302,7 +366,10 @@ openenv push --repo-id <your-hf-username>/adapt-dsa-tutor
302
 
303
  ## Links
304
 
305
- - HuggingFace Space URL: [HuggingFace Space URL]
306
- - Colab Training Notebook: [Colab Training Notebook]
307
- - HF Blog Post: [HF Blog Post]
308
- - YouTube Demo: [YouTube Demo]
 
 
 
 
15
 
16
  LLMs are getting better at one-shot code generation, but they still struggle with the thing real engineers do all day: read feedback, debug, and repair. ADAPT closes that gap by turning algorithm practice into a self-repair RL environment where the model must improve over multiple attempts instead of guessing once.
17
 
18
+ ## Submission links
19
+
20
+ - Hugging Face Space: [Dishaaa25/meta-rl-dsa-solver](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver)
21
+ - Live environment: [Dishaaa25-meta-rl-dsa-solver.hf.space](https://Dishaaa25-meta-rl-dsa-solver.hf.space)
22
+ - Training repo URL: [Space repository root](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/tree/main)
23
+ - Training script: [training/train_grpo.py](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/training/train_grpo.py)
24
+ - Training evidence: [TRAINING_EVIDENCE.md](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/TRAINING_EVIDENCE.md)
25
+ - Mini blog / writeup: [BLOG.md](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/BLOG.md)
26
+ - Trained adapter repo: [Dishaaa25/adapt-dsa-tutor-model](https://huggingface.co/Dishaaa25/adapt-dsa-tutor-model)
27
+ - Source repo mirror: [s-shah4/meta-rl-dsa-solver](https://github.com/s-shah4/meta-rl-dsa-solver)
28
+
29
  ## Why ADAPT exists
30
 
31
  Most code-generation benchmarks test whether a model can land the answer immediately. They do not test whether the model can recover from partial failure, use examples productively, or adapt as the task distribution changes.
 
198
 
199
  ## Results
200
 
201
+ We ran a real overnight GRPO training job on `Qwen/Qwen2.5-3B-Instruct` and logged `7,600` training episodes over `950` optimizer steps.
202
+
203
+ Training run:
204
+
205
+ - Run ID: `15940d1d-7d8c-4253-8810-2ea934bedee4`
206
+ - Started: `2026-04-25 22:21 UTC`
207
+ - Finished: `2026-04-26 07:19 UTC`
208
+ - Wall-clock time: `8.96 hours`
209
+ - Train preset: `overnight`
210
+ - Curriculum mode: `reward_aware`
211
+ - Trained model revision: `6c957e7c6bdb25ff086775fb8692570aee4501c9`
212
+
213
+ Proof of learning from the actual run logs:
214
+
215
+ - Average reward improved from `0.4441` in the first `500` episodes to `0.5951` in the last `500` episodes.
216
+ - Average hidden pass rate improved from `0.4625` to `0.6488`.
217
+ - Completion rate improved from `43.6%` to `59.6%`.
218
+
219
+ Late-training performance over the final `500` episodes:
220
+
221
+ | Difficulty | Episodes | Avg reward | Avg hidden pass rate | Completion rate |
222
+ | --- | ---: | ---: | ---: | ---: |
223
+ | Easy | 32 | 0.8390 | 0.8477 | 84.38% |
224
+ | Medium | 104 | 0.7892 | 0.8425 | 78.85% |
225
+ | Hard | 364 | 0.5182 | 0.5759 | 51.92% |
226
+
227
+ ### Reward curve
228
+
229
+ ![ADAPT reward curve](artifacts/reward_curve.svg)
230
+
231
+ ### Pass rate by difficulty
232
+
233
+ ![Pass rate by difficulty](artifacts/pass_rate_by_difficulty.svg)
234
+
235
+ ### Reward-aware family productivity
236
+
237
+ ![Family productivity](artifacts/family_productivity.svg)
238
+
239
+ Top productive families near the end of training:
240
+
241
+ - `smallest_most_frequent`
242
+ - `merge_intervals`
243
+ - `matrix_diagonal_sum`
244
+ - `fizzbuzz_variant`
245
+ - `group_anagrams_count`
246
+ - `max_subarray_sum`
247
+ - `two_sum_count`
248
+ - `balanced_brackets`
249
+
250
+ ### A concrete improvement story
251
+
252
+ This run did not include a separate baseline-eval sweep, so we do not claim a strict benchmark-style baseline-vs-trained score table. Instead, we show the model improving *inside the environment itself* over the same overnight run:
253
+
254
+ - early training is dominated by low-reward, low-pass-rate episodes and safety failures
255
+ - by the end of the run, the agent is regularly solving hard tasks like `reverse_words` with `reward=1.0`, `hidden pass rate=1.0`, and `efficiency score=1.0`
256
 
257
+ The raw summary used for the table above is stored in `artifacts/results_summary.json`.
258
 
259
+ For the hackathon submission form, the "Training Run Notebook URL" field can point to the public Hugging Face Space repository because this project trained directly on Hugging Face rather than from a separate Colab notebook. The repo includes:
260
 
261
+ - the full training entrypoint in `training/train_grpo.py`
262
+ - the recovered training evidence in `TRAINING_EVIDENCE.md`
263
+ - embedded reward / pass-rate plots committed as image files
264
+ - the lightweight structured summary in `artifacts/results_summary.json`
 
265
 
266
  ## How to run
267
 
 
366
 
367
  ## Links
368
 
369
+ - Hugging Face Space URL: [https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver)
370
+ - Live app URL: [https://Dishaaa25-meta-rl-dsa-solver.hf.space](https://Dishaaa25-meta-rl-dsa-solver.hf.space)
371
+ - Training repo URL: [https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/tree/main](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/tree/main)
372
+ - Training script URL: [https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/training/train_grpo.py](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/training/train_grpo.py)
373
+ - Training evidence URL: [https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/TRAINING_EVIDENCE.md](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/TRAINING_EVIDENCE.md)
374
+ - Blog post URL: [https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/BLOG.md](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/BLOG.md)
375
+ - Trained model URL: [https://huggingface.co/Dishaaa25/adapt-dsa-tutor-model](https://huggingface.co/Dishaaa25/adapt-dsa-tutor-model)
TRAINING_EVIDENCE.md ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Training Evidence
2
+
3
+ This project was trained directly on Hugging Face infrastructure rather than from a separate Colab notebook. For the hackathon submission form, the public Hugging Face Space repository can be used as the "Training Run Notebook URL" because it contains the runnable training script and the evidence artifacts below.
4
+
5
+ ## Public training assets
6
+
7
+ - Space repository root: [Dishaaa25/meta-rl-dsa-solver](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/tree/main)
8
+ - Training entrypoint: [training/train_grpo.py](https://huggingface.co/spaces/Dishaaa25/meta-rl-dsa-solver/blob/main/training/train_grpo.py)
9
+ - Trained adapter repo: [Dishaaa25/adapt-dsa-tutor-model](https://huggingface.co/Dishaaa25/adapt-dsa-tutor-model)
10
+ - Model revision from this run: `6c957e7c6bdb25ff086775fb8692570aee4501c9`
11
+
12
+ ## Confirmed run
13
+
14
+ - Run ID: `15940d1d-7d8c-4253-8810-2ea934bedee4`
15
+ - Status: `succeeded`
16
+ - Base model: `Qwen/Qwen2.5-3B-Instruct`
17
+ - Optimizer steps: `950`
18
+ - Logged training episodes: `7,600`
19
+ - Duration: `8.964 hours`
20
+ - Generator mode: `reward_aware`
21
+ - Dataset size: `1024`
22
+ - Checkpoint upload enabled: `true`
23
+
24
+ ## Evidence that training actually happened
25
+
26
+ - Reward curve image committed to the repo: `artifacts/reward_curve.svg`
27
+ - Pass-rate-by-difficulty image committed to the repo: `artifacts/pass_rate_by_difficulty.svg`
28
+ - Family-productivity image committed to the repo: `artifacts/family_productivity.svg`
29
+ - Structured summary committed to the repo: `artifacts/results_summary.json`
30
+
31
+ ## Key outcomes from the recovered run logs
32
+
33
+ - Average reward improved from `0.4441` over the first `500` episodes to `0.5951` over the last `500`.
34
+ - Average hidden pass rate improved from `0.4625` to `0.6488`.
35
+ - Completion rate improved from `43.6%` to `59.6%`.
36
+
37
+ Final-500-episode breakdown:
38
+
39
+ | Difficulty | Episodes | Avg reward | Avg hidden pass rate | Completion rate |
40
+ | --- | ---: | ---: | ---: | ---: |
41
+ | Easy | 32 | 0.8390 | 0.8477 | 84.38% |
42
+ | Medium | 104 | 0.7892 | 0.8425 | 78.85% |
43
+ | Hard | 364 | 0.5182 | 0.5759 | 51.92% |
44
+
45
+ ## Plots
46
+
47
+ ![Reward curve](artifacts/reward_curve.svg)
48
+
49
+ ![Pass rate by difficulty](artifacts/pass_rate_by_difficulty.svg)
50
+
51
+ ![Family productivity](artifacts/family_productivity.svg)
52
+
53
+ ## Training configuration snapshot
54
+
55
+ The full lightweight snapshot is committed in `artifacts/training_status_snapshot.json`. Key settings:
56
+
57
+ - learning rate: `5e-6`
58
+ - batch size: `1`
59
+ - gradient accumulation steps: `8`
60
+ - num generations: `4`
61
+ - max prompt length: `1024`
62
+ - max completion length: `384`
63
+ - LoRA rank: `16`
64
+ - LoRA alpha: `32`
65
+ - 4-bit loading: `true`
66
+
67
+ ## Why the repo URL is the right form entry
68
+
69
+ The hackathon form explicitly allows a public Hugging Face repository URL in place of a Colab notebook. Since this team trained on Hugging Face directly, the repository URL is the most accurate submission target: it contains the training code, the evidence plots, and the run summary in one public place.
artifacts/family_productivity.svg ADDED
artifacts/pass_rate_by_difficulty.svg ADDED
artifacts/results_summary.json ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "run_id": "15940d1d-7d8c-4253-8810-2ea934bedee4",
3
+ "episodes": 7600,
4
+ "train_steps_reported": 950,
5
+ "wall_clock_hours": 8.964,
6
+ "first_500": {
7
+ "avg_reward": 0.4441,
8
+ "avg_pass_rate": 0.4625,
9
+ "completion_rate": 0.436
10
+ },
11
+ "last_500": {
12
+ "avg_reward": 0.5951,
13
+ "avg_pass_rate": 0.6488,
14
+ "completion_rate": 0.596
15
+ },
16
+ "difficulty_last_500": {
17
+ "easy": {
18
+ "episodes": 32,
19
+ "avg_reward": 0.839,
20
+ "avg_pass_rate": 0.8477,
21
+ "completion_rate": 0.8438
22
+ },
23
+ "medium": {
24
+ "episodes": 104,
25
+ "avg_reward": 0.7892,
26
+ "avg_pass_rate": 0.8425,
27
+ "completion_rate": 0.7885
28
+ },
29
+ "hard": {
30
+ "episodes": 364,
31
+ "avg_reward": 0.5182,
32
+ "avg_pass_rate": 0.5759,
33
+ "completion_rate": 0.5192
34
+ }
35
+ },
36
+ "top_productive_families_final": [
37
+ "smallest_most_frequent",
38
+ "merge_intervals",
39
+ "matrix_diagonal_sum",
40
+ "fizzbuzz_variant",
41
+ "group_anagrams_count",
42
+ "max_subarray_sum",
43
+ "two_sum_count",
44
+ "balanced_brackets"
45
+ ]
46
+ }
artifacts/reward_curve.svg ADDED
artifacts/training_status_snapshot.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "status": "succeeded",
3
+ "run_id": "15940d1d-7d8c-4253-8810-2ea934bedee4",
4
+ "started_at": "2026-04-25T22:21:10.584881+00:00",
5
+ "finished_at": "2026-04-26T07:19:01.232852+00:00",
6
+ "model_repo_id": "Dishaaa25/adapt-dsa-tutor-model",
7
+ "uploaded_revision": "6c957e7c6bdb25ff086775fb8692570aee4501c9",
8
+ "phase": "completed",
9
+ "completed_steps": 950,
10
+ "total_steps": 950,
11
+ "train_episode_index": 7600,
12
+ "current_difficulty": "hard",
13
+ "last_problem_id": "reverse_words_8c8524b3",
14
+ "last_problem_family": "reverse_words",
15
+ "last_pass_rate": 1.0,
16
+ "last_visible_pass_rate": 1.0,
17
+ "last_reward": 1.0,
18
+ "last_execution_status": "completed",
19
+ "timing_summary": {
20
+ "wall_clock_seconds": 32254.3,
21
+ "wall_clock_minutes": 537.57,
22
+ "wall_clock_hours": 8.96,
23
+ "completed_steps": 950,
24
+ "train_episode_count": 7600,
25
+ "configured_dataset_size": 1024,
26
+ "configured_batch_size": 1,
27
+ "configured_gradient_accumulation_steps": 8,
28
+ "configured_num_generations": 4,
29
+ "avg_seconds_per_step": 33.95,
30
+ "steps_per_hour": 106.03,
31
+ "avg_seconds_per_episode": 4.24,
32
+ "episodes_per_hour": 848.26
33
+ },
34
+ "config": {
35
+ "model_name": "Qwen/Qwen2.5-3B-Instruct",
36
+ "dataset_size": 1024,
37
+ "max_steps": 950,
38
+ "batch_size": 1,
39
+ "gradient_accumulation_steps": 8,
40
+ "num_generations": 4,
41
+ "max_seq_length": 2048,
42
+ "max_prompt_length": 1024,
43
+ "max_completion_length": 384,
44
+ "learning_rate": 5e-6,
45
+ "lora_rank": 16,
46
+ "lora_alpha": 32,
47
+ "load_in_4bit": true,
48
+ "gradient_checkpointing": true,
49
+ "bf16": false,
50
+ "baseline_eval": false,
51
+ "evaluation_episodes": 20,
52
+ "disable_wandb": true,
53
+ "generator_mode": "reward_aware",
54
+ "trace_logging_enabled": true,
55
+ "save_steps": 50,
56
+ "save_total_limit": 3,
57
+ "upload_checkpoints_to_hub": true
58
+ }
59
+ }
requirements.txt CHANGED
@@ -7,3 +7,4 @@ huggingface_hub>=0.34.0,<0.36
7
  datasets>=2.18.0
8
  transformers>=4.51.3,<5.7
9
  peft>=0.18.0,<0.20
 
 
7
  datasets>=2.18.0
8
  transformers>=4.51.3,<5.7
9
  peft>=0.18.0,<0.20
10
+ unsloth==2026.3.5
server/requirements.txt CHANGED
@@ -6,3 +6,4 @@ httpx>=0.28.0
6
  huggingface_hub>=0.34.0,<0.36
7
  transformers>=4.51.3,<5.7
8
  peft>=0.18.0,<0.20
 
 
6
  huggingface_hub>=0.34.0,<0.36
7
  transformers>=4.51.3,<5.7
8
  peft>=0.18.0,<0.20
9
+ unsloth==2026.3.5
server/runtime.py CHANGED
@@ -174,6 +174,32 @@ class SpaceModelRegistry:
174
  ) from exc
175
  return torch, AutoPeftModelForCausalLM, (AutoModelForCausalLM, AutoTokenizer)
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  def _base_model_name(self) -> str:
178
  return os.getenv("BASE_MODEL_NAME") or os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-3B-Instruct"
179
 
@@ -264,15 +290,23 @@ class SpaceModelRegistry:
264
  dtype = torch.float16
265
  else:
266
  dtype = torch.float32
267
- tokenizer = AutoTokenizer.from_pretrained(base_model_name)
268
- if tokenizer.pad_token is None and tokenizer.eos_token is not None:
269
- tokenizer.pad_token = tokenizer.eos_token
270
- model = AutoModelForCausalLM.from_pretrained(
271
- base_model_name,
272
- device_map="auto",
273
- torch_dtype=dtype,
274
  )
275
- model.eval()
 
 
 
 
 
 
 
 
 
 
 
276
  self._base_model = model
277
  self._base_tokenizer = tokenizer
278
  self._set_state(
@@ -307,17 +341,27 @@ class SpaceModelRegistry:
307
  dtype = torch.float16
308
  else:
309
  dtype = torch.float32
310
- tokenizer = AutoTokenizer.from_pretrained(str(artifact_dir))
311
- if tokenizer.pad_token is None and tokenizer.eos_token is not None:
312
- tokenizer.pad_token = tokenizer.eos_token
313
-
314
  if (artifact_dir / "adapter_config.json").exists():
315
- model = AutoPeftModelForCausalLM.from_pretrained(
316
- str(artifact_dir),
317
- device_map="auto",
318
- torch_dtype=dtype,
319
  )
 
 
 
 
 
 
 
 
 
 
 
320
  else:
 
 
 
321
  model = AutoModelForCausalLM.from_pretrained(
322
  str(artifact_dir),
323
  device_map="auto",
 
174
  ) from exc
175
  return torch, AutoPeftModelForCausalLM, (AutoModelForCausalLM, AutoTokenizer)
176
 
177
+ def _load_with_unsloth(
178
+ self,
179
+ *,
180
+ model_name: str,
181
+ dtype: Any,
182
+ load_in_4bit: bool,
183
+ max_seq_length: int = 2048,
184
+ ) -> tuple[Any, Any] | None:
185
+ try:
186
+ from unsloth import FastLanguageModel
187
+ except ImportError:
188
+ return None
189
+
190
+ model, tokenizer = FastLanguageModel.from_pretrained(
191
+ model_name=model_name,
192
+ max_seq_length=max_seq_length,
193
+ dtype=dtype,
194
+ load_in_4bit=load_in_4bit,
195
+ )
196
+ if tokenizer.pad_token is None and tokenizer.eos_token is not None:
197
+ tokenizer.pad_token = tokenizer.eos_token
198
+ if hasattr(FastLanguageModel, "for_inference"):
199
+ FastLanguageModel.for_inference(model)
200
+ model.eval()
201
+ return model, tokenizer
202
+
203
  def _base_model_name(self) -> str:
204
  return os.getenv("BASE_MODEL_NAME") or os.getenv("MODEL_NAME") or "Qwen/Qwen2.5-3B-Instruct"
205
 
 
290
  dtype = torch.float16
291
  else:
292
  dtype = torch.float32
293
+ unsloth_stack = self._load_with_unsloth(
294
+ model_name=base_model_name,
295
+ dtype=dtype,
296
+ load_in_4bit=torch.cuda.is_available(),
 
 
 
297
  )
298
+ if unsloth_stack is not None:
299
+ model, tokenizer = unsloth_stack
300
+ else:
301
+ tokenizer = AutoTokenizer.from_pretrained(base_model_name)
302
+ if tokenizer.pad_token is None and tokenizer.eos_token is not None:
303
+ tokenizer.pad_token = tokenizer.eos_token
304
+ model = AutoModelForCausalLM.from_pretrained(
305
+ base_model_name,
306
+ device_map="auto",
307
+ torch_dtype=dtype,
308
+ )
309
+ model.eval()
310
  self._base_model = model
311
  self._base_tokenizer = tokenizer
312
  self._set_state(
 
341
  dtype = torch.float16
342
  else:
343
  dtype = torch.float32
 
 
 
 
344
  if (artifact_dir / "adapter_config.json").exists():
345
+ unsloth_stack = self._load_with_unsloth(
346
+ model_name=str(artifact_dir),
347
+ dtype=dtype,
348
+ load_in_4bit=torch.cuda.is_available(),
349
  )
350
+ if unsloth_stack is not None:
351
+ model, tokenizer = unsloth_stack
352
+ else:
353
+ tokenizer = AutoTokenizer.from_pretrained(str(artifact_dir))
354
+ if tokenizer.pad_token is None and tokenizer.eos_token is not None:
355
+ tokenizer.pad_token = tokenizer.eos_token
356
+ model = AutoPeftModelForCausalLM.from_pretrained(
357
+ str(artifact_dir),
358
+ device_map="auto",
359
+ torch_dtype=dtype,
360
+ )
361
  else:
362
+ tokenizer = AutoTokenizer.from_pretrained(str(artifact_dir))
363
+ if tokenizer.pad_token is None and tokenizer.eos_token is not None:
364
+ tokenizer.pad_token = tokenizer.eos_token
365
  model = AutoModelForCausalLM.from_pretrained(
366
  str(artifact_dir),
367
  device_map="auto",