rhino-coder-7b / README.md
quocvibui's picture
Upload folder using huggingface_hub
7dce080 verified
---
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)