File size: 5,875 Bytes
7dce080
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
---
license: apache-2.0
base_model: Qwen/Qwen2.5-Coder-7B-Instruct
library_name: mlx
tags:
  - mlx
  - lora
  - code
  - rhino3d
  - rhinoscriptsyntax
  - rhinocommon
  - 3d-modeling
  - cad
  - python
datasets:
  - custom
language:
  - en
pipeline_tag: text-generation
model-index:
  - name: rhino-coder-7b
    results: []
---

# Rhino Coder 7B

A fine-tuned [Qwen2.5-Coder-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct) model specialized for **Rhino3D Python scripting** β€” generating correct `rhinoscriptsyntax` and `RhinoCommon` code from natural language instructions.

This is the **fused model** (LoRA weights merged into base). For the standalone LoRA adapter, see [rhino-coder-7b-lora](https://huggingface.co/quocvibui/rhino-coder-7b-lora).

## Why Fine-Tune?

The base Qwen2.5-Coder-7B is a strong general code model, but it doesn't know Rhino's APIs. On 10 held-out Rhino scripting tasks:

| Metric | Base Model | Fine-Tuned | Delta |
|--------|-----------|------------|-------|
| Avg code lines | 11.9 | 8.2 | -3.7 (more concise) |
| Avg code chars | 427 | 258 | -40% less bloat |

- **Base model hallucinates APIs** β€” invents `Rhino.Commands.Command.AddPoint()`, `rs.filter.surface`, `rg.PipeSurface.Create()` β€” none of these exist
- **Fine-tuned uses correct APIs** β€” `rs.CurveAreaCentroid()`, `rs.AddPipe()`, `rs.GetObject("...", 8)` with the right filter constants
- **Fine-tuned matches reference style** β€” several outputs are near-identical to the reference solutions

### Example β€” *"How do I find the centroid of a closed curve?"*

```python
# BASE MODEL β€” wrong (averages control points, not area centroid)
def find_centroid(curve_id):
    points = rs.CurvePoints(curve_id)
    centroid = [0, 0, 0]
    for point in points:
        centroid[0] += point[0]
        centroid[1] += point[1]
        centroid[2] += point[2]
    centroid[0] /= len(points)
    return centroid

# FINE-TUNED β€” correct, concise
crv = rs.GetObject('Select closed curve', 4)
if crv and rs.IsCurveClosed(crv):
    centroid = rs.CurveAreaCentroid(crv)
    if centroid:
        rs.AddPoint(centroid[0])
```

## Usage

### With MLX (Apple Silicon)

```bash
pip install mlx-lm
```

```python
from mlx_lm import load, generate

model, tokenizer = load("quocvibui/rhino-coder-7b")

messages = [
    {"role": "system", "content": "You are an expert Rhino3D Python programmer. Write clean, working scripts using rhinoscriptsyntax and RhinoCommon. Include all necessary imports. Only output code, no explanations unless asked."},
    {"role": "user", "content": "Create a 10x10 grid of spheres with radius 0.5"},
]

prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
output = generate(model, tokenizer, prompt=prompt, max_tokens=1024)
print(output)
```

### As an OpenAI-compatible server

```bash
mlx_lm server --model quocvibui/rhino-coder-7b --port 8080
```

Then query it like any OpenAI-compatible API:

```python
import requests

response = requests.post("http://localhost:8080/v1/chat/completions", json={
    "model": "default",
    "messages": [
        {"role": "system", "content": "You are an expert Rhino3D Python programmer. Write clean, working scripts using rhinoscriptsyntax and RhinoCommon. Include all necessary imports. Only output code, no explanations unless asked."},
        {"role": "user", "content": "Draw a spiral staircase with 20 steps"}
    ],
    "max_tokens": 1024,
    "temperature": 0.1
})
print(response.json()["choices"][0]["message"]["content"])
```

## Training Details

### Method

LoRA (Low-Rank Adaptation) fine-tuning via [MLX-LM](https://github.com/ml-explore/mlx-examples), then fused into the base model.

### Hyperparameters

| Parameter | Value |
|-----------|-------|
| Base model | Qwen2.5-Coder-7B-Instruct (4-bit) |
| Method | LoRA |
| LoRA rank | 8 |
| LoRA scale | 20.0 |
| LoRA dropout | 0.0 |
| LoRA layers | 16 / 28 |
| Batch size | 1 |
| Learning rate | 1e-5 |
| Optimizer | Adam |
| Max sequence length | 2,048 |
| Iterations | 9,108 (2 epochs) |
| Validation loss | 0.184 |
| Training time | ~1.2 hours on M2 Max |

### Dataset

5,060 instruction-code pairs for Rhino3D Python scripting (90/10 train/val split):

| Source | Count |
|--------|-------|
| RhinoCommon API docs | 1,355 |
| RhinoScriptSyntax source | 926 |
| Official samples | 93 |
| Synthetic generation | 187 |
| Backlabeled GitHub | 1 |

**API coverage:**

| API | Pairs |
|-----|-------|
| RhinoCommon | 1,409 |
| rhinoscriptsyntax | 1,134 |
| rhino3dm | 18 |
| compute | 1 |

Data was cleaned aggressively β€” 10,252 entries excluded from 12,814 total raw entries. Filters removed trivial getters, boilerplate, placeholder code, C#-only types, and duplicates.

### Chat format

```json
{
  "messages": [
    {"role": "system", "content": "You are an expert Rhino3D Python programmer..."},
    {"role": "user", "content": "<instruction>"},
    {"role": "assistant", "content": "<python code>"}
  ]
}
```

## Intended Use

- Generating Python scripts for Rhino3D (rhinoscriptsyntax / RhinoCommon)
- Computational design and 3D modeling automation
- Interactive code generation in a Rhino 8 REPL workflow

## Limitations

- Trained on Rhino3D Python APIs only β€” not a general-purpose coding model
- Best results with rhinoscriptsyntax (`rs.*`) and RhinoCommon (`Rhino.Geometry.*`)
- May not cover every API method β€” training data focused on the most commonly used patterns
- Quantized to 4-bit β€” some precision tradeoffs vs. full-precision models
- Optimized for MLX on Apple Silicon; for GPU inference, you may need to convert weights

## Links

- [GitHub: rhino3d-SLM](https://github.com/quocvibui/rhino3d-SLM)
- [LoRA Adapter](https://huggingface.co/quocvibui/rhino-coder-7b-lora)
- [Base model: Qwen2.5-Coder-7B-Instruct](https://huggingface.co/Qwen/Qwen2.5-Coder-7B-Instruct)