Rhino Coder 7B
A fine-tuned 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.
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?"
# 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)
pip install mlx-lm
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
mlx_lm server --model quocvibui/rhino-coder-7b --port 8080
Then query it like any OpenAI-compatible API:
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, 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
{
"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
- Downloads last month
- 49
Quantized