# Using the Cadpy Assembly Composition API for Multi-Part CAD Models

> Learn to build hierarchical JSON for multi-part CAD assemblies using the cadpy assembly composition API. Programmatically generate complex mechanical designs from STEP or GLB files.

- Repository: [earthtojake/text-to-cad](https://github.com/earthtojake/text-to-cad)
- Tags: api-reference
- Published: 2026-08-01

---

**The cadpy assembly composition API builds hierarchical JSON representations of multi-part CAD assemblies from STEP files or GLB topology, enabling programmatic generation of complex mechanical designs.**

The **earthtojake/text-to-cad** repository provides the **cadpy** package, a low-level Python toolkit for procedural CAD generation. At its core, the assembly composition API—implemented across [`cadpy/assembly_composition.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_composition.py), [`cadpy/assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_spec.py), and [`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py)—enables developers to aggregate individual parts into unified assemblies with preserved spatial transforms and hierarchical relationships.

## Architecture of the Assembly Composition System

The API follows a three-layer architecture that separates data description from scene construction and export orchestration.

### Assembly Specification Layer

The **assembly specification** defines the structural blueprint of your multi-part model. Defined in [`cadpy/assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_spec.py), the `AssemblySpec` dataclass stores component references, local coordinate transforms, and nested sub-assembly relationships. Each component is represented by an `AssemblyComponent` instance that points to a source STEP file and defines its orientation in 3D space via a 12-element affine transform matrix.

### Assembly Composition Layer

The **composition engine** in [`cadpy/assembly_composition.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_composition.py) translates specifications into traversable scene graphs. Two primary builders handle different source topologies:

- **`build_linked_assembly_composition`** – Resolves `AssemblySpec` trees into linked hierarchies where each node references its original STEP source file.
- **`build_native_assembly_composition`** – Reconstructs assemblies from existing GLB files that already contain embedded occurrence data.

Internal helpers such as `_linked_instance_node` and `_native_occurrence_node` manage the conversion of raw topology into JSON-ready node dictionaries.

### Generation Pipeline Layer

The **orchestration layer** in [`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py) provides `_assembly_composition_for_spec`, which automatically selects the appropriate builder based on the specification's `kind` field. This function returns a complete scene dictionary that can be passed directly to STEP export utilities or viewer payloads.

## Building a Multi-Part Assembly Step-by-Step

### Step 1: Define the Assembly Specification

Create an `AssemblySpec` that references individual STEP files and defines their relative positioning. In [`cadpy/assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_spec.py), the specification acts as the single source of truth for assembly structure.

```python
from pathlib import Path
from cadpy.assembly_spec import AssemblySpec, AssemblyComponent

# Define a root assembly containing two translated parts

spec = AssemblySpec(
    kind="assembly",
    assembly_path=Path("models/assembly_example"),
    step_path=None,  # Linked composition does not require a root STEP

    components=[
        AssemblyComponent(
            source_path=Path("models/part_a.step"),
            instance_id="part_a",
            transform=[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0],  # Identity matrix

        ),
        AssemblyComponent(
            source_path=Path("models/part_b.step"),
            instance_id="part_b",
            transform=[1, 0, 0, 10, 0, 1, 0, 0, 0, 0, 1, 0],  # Translate 10mm on X

        ),
    ],
)

```

### Step 2: Build the Linked Composition

Call `build_linked_assembly_composition` from [`cadpy/assembly_composition.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_composition.py) to resolve the specification into a hierarchical node structure. This function requires a topology manifest—typically loaded from a GLB file—and a mapping of STEP paths to their specifications.

```python
from pathlib import Path
from cadpy.assembly_composition import build_linked_assembly_composition
from cadpy.glb_topology import read_step_topology_manifest_from_glb
from cadpy.render import part_glb_path, relative_to_repo

# Load the GLB topology manifest for the root assembly container

topology_path = part_glb_path(Path("models/assembly_example.glb"))
topology_manifest = read_step_topology_manifest_from_glb(topology_path)

# Map source STEP files to their specifications for nested resolution

entries_by_step_path = {
    Path("models/part_a.step"): spec,
    Path("models/part_b.step"): spec
}

composition = build_linked_assembly_composition(
    cad_ref=relative_to_repo(Path("models/assembly_example")),
    topology_path=topology_path,
    topology_manifest=topology_manifest,
    assembly_spec=spec,
    entries_by_step_path=entries_by_step_path,
    read_assembly_spec=lambda p: spec,  # Resolver function for nested specs

    mesh_path=topology_path,
)

```

### Step 3: Generate the Scene with the Pipeline

Use `_assembly_composition_for_spec` from [`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py) to automate builder selection and scene assembly. This internal helper handles the conditional logic between linked and native composition modes.

```python
from cadpy.generation import _assembly_composition_for_spec

# Payload defines the assembly type and root path

payload = {"kind": "assembly", "assemblyPath": "models/assembly_example"}

# Generate the complete scene dictionary with root node and children

scene = _assembly_composition_for_spec(
    spec=spec,
    cad_ref="assembly_example",
    topology_path=topology_path,
    topology_manifest=topology_manifest,
    entries_by_step_path=entries_by_step_path,
    read_assembly_spec=lambda p: spec,
    mesh_path=topology_path,
)

# The scene dict contains a 'root' node with nested children for part_a and part_b

```

### Step 4: Export to STEP Format

Feed the composed scene to `export_assembly_step_scene` in [`cadpy/step_export.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/step_export.py) to generate the final CAD file. This function handles mesh URL resolution, topology validation, and hash computation for downstream tools.

```python
from cadpy.step_export import export_assembly_step_scene
from pathlib import Path

export_assembly_step_scene(
    assembly_spec=spec,
    scene=scene,
    output_path=Path("out/assembly_example.step"),
    text_to_cad_entry_kind="assembly",
)

```

## Key Implementation Files

The assembly composition system spans four critical modules in the earthtojake/text-to-cad codebase:

- **[`cadpy/assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_spec.py)** – Defines `AssemblySpec` and `AssemblyComponent` dataclasses, plus `read_assembly_spec` for JSON/YAML deserialization.
- **[`cadpy/assembly_composition.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_composition.py)** – Implements `build_linked_assembly_composition` and `build_native_assembly_composition`, along with private helpers `_linked_instance_node` and `_native_occurrence_node` for node construction.
- **[`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py)** – Contains `_assembly_composition_for_spec` for pipeline orchestration and `_write_assembly_step_payload` for final file serialization.
- **[`cadpy/step_export.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/step_export.py)** – Provides `export_assembly_step_scene` and `read_step_topology_manifest_from_glb` for topology introspection and STEP generation.

## Summary

- The **cadpy assembly composition API** transforms discrete STEP files into unified, hierarchical CAD assemblies through a three-stage pipeline.
- **`AssemblySpec`** defines the assembly structure, component transforms, and source file references in [`cadpy/assembly_spec.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/assembly_spec.py).
- **`build_linked_assembly_composition`** constructs scene graphs by linking external STEP files, while `build_native_assembly_composition` processes embedded GLB topology.
- **`_assembly_composition_for_spec`** in [`cadpy/generation.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/generation.py) orchestrates the build process and prepares data for export.
- **`export_assembly_step_scene`** generates the final STEP file with proper topology manifests and mesh references.

## Frequently Asked Questions

### What is the difference between linked and native assembly composition?

**Linked composition** (`build_linked_assembly_composition`) assembles parts by referencing external STEP files defined in an `AssemblySpec`, making it ideal for aggregating discrete components. **Native composition** (`build_native_assembly_composition`) reconstructs assemblies from GLB files that already contain internal occurrence trees, useful for reprocessing existing models. Both methods produce identical JSON scene graphs but source their topology data differently.

### How do I specify the position of individual parts in an assembly?

Each `AssemblyComponent` accepts a 12-element `transform` list representing a 4×3 affine transformation matrix in row-major order. The first nine elements define the rotation/scale matrix, while the final three elements specify the translation vector in millimeters. This transform places the component relative to the parent assembly's coordinate system.

### Can I nest assemblies within other assemblies using this API?

Yes. The `entries_by_step_path` mapping allows `build_linked_assembly_composition` to resolve nested `AssemblySpec` instances. When a component's `source_path` points to another assembly rather than a part, the resolver function (passed as `read_assembly_spec`) returns the sub-assembly specification, enabling arbitrary depth in the scene hierarchy.

### What file formats does the assembly composition API support for export?

The primary export format is **STEP** (`.step`), generated via `export_assembly_step_scene` in [`cadpy/step_export.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/step_export.py). The API also produces intermediate **GLB** payloads for viewer visualization, handled by `part_glb_path` utilities in [`cadpy/glb.py`](https://github.com/earthtojake/text-to-cad/blob/main/cadpy/glb.py). The composition itself is format-agnostic, returning a JSON tree that can be serialized to either format or streamed to web viewers.