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