AutoAssembler Python SDK
AutoAssembler is C-Infinity's foundational AI for assembly process planning: read a CAD assembly, analyze its geometry, motion and production logic, and turn that into manufacturing process plans, diff analysis, DFA analysis, work instructions and more. This package is the official Python client for the AutoAssembler API. From a script or a notebook you can:
- Upload a CAD assembly (STEP, PVZ) and get a processed
Assemblyback. - Plan it: create a process plan, assign parts to stations, let the planner work out the assembly sequence, then validate that every station is physically buildable.
- Diff two revisions of an assembly and adapt an existing plan to the new one.
Status: 0.x. The surface may still change between minor releases; see Versioning. The v4 API is stable, except the operations under
client.alpha.
Documentation
- The Quickstart below, then the Examples for each task in more depth.
- Every public class and method has a docstring:
help(client.planning),help(mpp.plan), or your editor's hover. autoassembler --helpfor the command line.
Installation
pip install autoassembler # or: uv add autoassembler
pip install 'autoassembler[cli]' # also the `autoassembler` command (see Command line)
Requires Python 3.12+.
Authentication
Create an API key in the AutoAssembler app under
Settings → API keys → Create API key, then either pass it explicitly, set
AA_TOKEN, or log in interactively once:
from autoassembler import AutoAssembler
client = AutoAssembler(token="aa_...") # explicit (CI, scripts)
client = AutoAssembler() # reads AA_TOKEN, or the token stored by login()
import autoassembler
_ = autoassembler.login() # confirm in your browser; stores the token for 30 days
login() stores tokens in ~/.config/autoassembler/credentials.json; logout() forgets them.
login() also returns the token: assign its result as above to avoid displaying it in a
notebook or interactive shell. Do not commit credentials or include them in reports.
logout() removes the local copy; it does not revoke the token on the server.
Pass base_url= to both login() and AutoAssembler() for anything but https://app.autoassembler.com,
including your own deployment.
Quickstart
Use your own CAD files in place of the example filenames below; no CAD data is bundled. These calls upload the files to the configured service and create resources in your account.
from autoassembler import AutoAssembler
client = AutoAssembler()
# Upload two revisions of a design. Processing overlaps on the server; wait for both.
v1, v2 = (op.result() for op in (client.assemblies.upload("gearbox_v1.step"),
client.assemblies.upload("gearbox_v2.step")))
# Plan v1 from scratch: one station with every part, plan it, check it is buildable.
mpp = client.planning.create_mpp(v1, name="Gearbox v1") # a manufacturing process plan
station = mpp.add_station("Final assembly")
outcome = mpp.plan(station).result() # complete | partial | failed
verdict = mpp.validate(station).result() # feasible | infeasible
print(outcome[station].status, verdict[station].status, mpp.viewer_url)
# What changed in v2, and v1's plan carried over to it.
diff = client.assemblies.diff(v1, v2).result()
mpp_v2 = client.planning.adapt(mpp, v2, diff=diff).result()
Examples
Each example below assumes client = AutoAssembler() and a valid token (see
Authentication). Long-running calls take a timeout in seconds;
OperationTimeout means the work is still running on the server, not that it failed.
Upload several CAD files
upload() returns as soon as the file is transferred, with processing under way on the server.
Start every upload before waiting on any, and the processing overlaps:
from autoassembler import AutoAssembler, OperationFailed, OperationTimeout
client = AutoAssembler()
paths = ["gearbox_v1.step", "gearbox_v2.step", "housing.pvz"] # .step, .stp or .pvz
ops = [client.assemblies.upload(path) for path in paths]
for path, op in zip(paths, ops):
try:
assembly = op.result(timeout=1800)
except (OperationFailed, OperationTimeout) as error: # e.g. the file is not an assembly
print(f"{path}: {error}")
continue
print(f"{path} → {assembly.id} {assembly.viewer_url}")
Work with existing assemblies and plans
Everything you created earlier is reachable by id:
assembly = client.assemblies.get("asm-1")
print(assembly.name, assembly.status) # processing | ready | failed
for mpp in assembly.plans(): # every process plan of the assembly
print(mpp.id, mpp.name)
mpp = client.planning.get_mpp(assembly, "mpp-1")
structure = assembly.structure() # the processed eBOM
connections = assembly.connections() # part-to-part connections (liaison graph)
Compare two revisions
Both assemblies must be ready. The report's summary holds the counts and details the
parts behind them:
report = client.assemblies.diff("asm-1", "asm-2").result(timeout=1800) # handles or ids
summary = report.summary
if summary.total_parts:
print(f"parts: {summary.total_parts.reference} → {summary.total_parts.target}")
if summary.geometric_diffs:
print(f"only in v1: {summary.geometric_diffs.reference}")
print(f"only in v2: {summary.geometric_diffs.target}")
for swap in report.details.swaps or []:
print(f"swap: {swap.reference_part} → {swap.target_part} ({swap.confidence})")
for moved in report.details.moved_pose_pairs or []:
print(f"moved: {moved.reference_part}")
Plan, validate and score a process plan
A new plan knows the assembly's parts and has no stations. add_station takes every
unassigned part by default, or the subset you pass as parts=:
mpp = client.planning.create_mpp(assembly, name="Gearbox — two stations")
print(len(mpp.unassigned_parts), "parts to assign")
subassembly = mpp.add_station("Shaft subassembly", parts=mpp.unassigned_parts[:5])
final = mpp.add_station("Final assembly") # everything left
outcome = mpp.plan(*mpp.stations).result(timeout=1800) # one or more stations
for station in mpp.stations:
status = outcome[station]
print(station.name, status.status) # complete | partial | failed
for message in status.messages: # what a partial plan could not place
print(" -", message)
verdict = mpp.validate().result(timeout=1800) # no arguments: every station
for station in mpp.stations:
if station in verdict: # stations with no planned steps are skipped
print(station.name, verdict[station].status, verdict[station].messages)
if verdict.ok: # every station feasible
dfa = mpp.evaluate_dfa().result(timeout=1800) # design-for-assembly scores
for station_id, report in dfa.items():
print(mpp.station(station_id).name, report.total_score, report.average_score)
print(mpp.viewer_url) # open the plan in the app
outcome.ok is true when every station planned complete. evaluate_dfa needs stations
that are planned and feasible.
Carry a plan over to a new revision
Rather than planning a revision from scratch, adapt the previous revision's plan: stations and steps carry over, adjusted for what changed.
v1, v2 = client.assemblies.get("asm-1"), client.assemblies.get("asm-2")
plan_v1 = client.planning.get_mpp(v1, "mpp-1")
diff = client.assemblies.diff(v1, v2).result(timeout=1800)
try:
plan_v2 = client.planning.adapt(plan_v1, v2, diff=diff, name="Gearbox v2").result(timeout=1800)
except OperationFailed as error:
raise SystemExit(f"adaptation failed: {error.message}")
for station in plan_v2.stations:
print(station.name, station.status)
Resume an operation from another process
Every Operation has a name. Store it, and pick the operation up later, from any process,
without starting the work again:
op = mpp.plan(station)
print(op.name) # e.g. "operations/…"; save it somewhere
# later, elsewhere
from autoassembler import AutoAssembler
op = AutoAssembler().operations.handle(saved_name)
if op.done():
print(op.result())
A resumed operation returns the raw API response rather than the typed result
(PlanOutcome, Assembly, …); refresh the plan or look the assembly up to read it.
Clean up
mpp.delete() # a process plan
assembly.delete() # an assembly (only your own uploads)
Command line
The cli extra installs the autoassembler command, for shell scripts and agents. Each command
is a thin layer over the same calls (uvx --from 'autoassembler[cli]' autoassembler runs it
without installing):
autoassembler login # once; or set AA_TOKEN
autoassembler status # versions, server, and who you are logged in as
autoassembler assemblies upload gearbox_v1.step gearbox_v2.step # one `ID STATUS FILE` row each
mpp=$(autoassembler plans create asm-1 --name "Gearbox v1") # creating commands print the id
autoassembler plans add-station asm-1 "$mpp" "Final assembly"
autoassembler plans plan asm-1 "$mpp" # every station; or name some, by id or name
autoassembler plans validate asm-1 "$mpp" --json # one JSON document on stdout
autoassembler assemblies diff asm-1 asm-2
autoassembler plans adapt asm-1 "$mpp" asm-2
autoassembler --help (or -h), and the same on any command, lists the rest. Conventions:
- Results go to stdout and progress to stderr.
--jsonprints the result as one JSON document, with this SDK's field names. - Exit codes:
0success;1the request ran and the result is a failure (an API error, a failed operation, a station that failed to plan, an infeasible station);2usage;3not authenticated;4still running when--timeoutran out (it keeps running on the server). - Long-running commands wait (up to
--timeout, 1800 s by default).--no-waitprints the operation's name instead, forautoassembler operations wait NAMElater. - The server is
https://app.autoassembler.comunless--urlorAA_URLsays otherwise.
How the SDK is organized
-
Concepts.
Assembly(a CAD file the service knows) andProcessPlan(a manufacturing process plan for one assembly, made ofStations). -
Actions.
upload,diff,plan,validate,evaluate_dfa,adapt. An action that creates a resource lives on a namespace (client.assemblies.upload,client.planning.adapt); an action that acts on an existing resource lives on that resource's handle (mpp.plan(station)). -
Waiting. Anything that outlives its HTTP request returns an
Operation[T].op.result(timeout=None)polls with backoff and returnsT, or raisesOperationFailed/OperationTimeout;op.done()checks without blocking. Start several, then collect:ops = [client.assemblies.upload(p) for p in cad_files] assemblies = [op.result() for op in ops]
Outcomes use the document's own words: a station's planning status is pending, complete,
partial (a usable partial plan) or failed; its validation verdict is feasible or
infeasible, with the issues behind it; an assembly is processing, ready or failed.
The full API: client.api
The curated layer above is built on a client generated from the v4 OpenAPI spec, available
as client.api (client.api.assemblies.get_status(...), client.api.planning.get_mpp(...)).
Use it when the vocabulary above does not cover what you need, and
tell us what was missing. Every endpoint method carries its own docstring
(help(client.api.planning.get_mpp)), and the whole tree is browsable from help(client.api).
Errors
| Failure | Raised as |
|---|---|
| The server answered with an error status | ApiError (status_code, body, headers), or a typed subclass: BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, ConflictError, UnprocessableEntityError |
| The response did not match the expected schema | ParsingError (not an ApiError) |
| No response at all (DNS, connection, timeout) | httpx.HTTPError subclasses |
| A long-running operation failed or was cancelled | OperationFailed (code, message, operation) |
| A long-running operation did not finish in time | OperationTimeout (a TimeoutError) |
Async
AsyncAutoAssembler currently exposes the generated client (client.api) and
client.operations; the curated assemblies / planning namespaces arrive on the async
client in a later release under the same names, with asyncio.gather as the natural way to
run work in parallel.
Advanced
- Timeouts. 60 s per request by default:
AutoAssembler(timeout=20.0)orrequest_options={"timeout": 5}per call. - Retries. Requests that fail with 408, 409, 429 or 5xx are retried twice with backoff;
request_options={"max_retries": 0}turns it off per call. - Your own
httpxclient, for proxies or transports:AutoAssembler(httpx_client=httpx.Client(proxy="http://...")). - Raw responses.
client.api.<namespace>.with_raw_response.<method>()returns headers and status alongside the parsed data. - Context managers.
with AutoAssembler() as client:closes the connection pool.
Compatibility
| API | AutoAssembler v4 (autoassembler.API_VERSION == "v4"). The client is generated from the v4 OpenAPI spec. |
| Servers | https://app.autoassembler.com by default; any AutoAssembler deployment that serves the v4 API via base_url=. |
Versioning
The package follows semantic versioning, independently of the API
version. While 0.x, a minor release may change the surface and a patch release only fixes;
from 1.0, breaking changes need a major release and removals are deprecated for one minor
release first. Pin a minor version (autoassembler~=0.4.0) if you need a stable surface
while on 0.x.
Alpha
Operations the API offers before they are stable sit under client.alpha (also
client.api.alpha): today client.alpha.jobs and client.alpha.planning.apply_ops. They are
outside these guarantees: any release may change or remove them. An operation that becomes
stable moves out of alpha, to its namespace's usual place.
Support
Bug reports, feature requests and questions: write to
info@c-infinity.ai. For a bug, include the SDK version
(python -c "import autoassembler; print(autoassembler.__version__)"), your Python version,
and, for a failed operation, its name. Do not include API keys or tokens.
License
MIT © C-Infinity. The license text ships with the package.
Metadata
Release files for autoassembler 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| autoassembler-0.4.1.tar.gz | 148.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| autoassembler-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 352.9 kB
Release files / autoassembler-0.4.1.tar.gz
| Download URL | autoassembler-0.4.1.tar.gz |
|---|---|
| Size | 148.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ef24d35c23ff8b6752543491ce410dae9c8d3ba2d7aa62cdbe1873b914929868
|
|
BLAKE2b-256 checksum How to use checksums |
e0061182e0f1c012d32d5ac9548915f7b4af7104b2d74eb61571d0a283ce3bd4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / autoassembler-0.4.1-py3-none-any.whl
| Download URL | autoassembler-0.4.1-py3-none-any.whl |
|---|---|
| Size | 204.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
706d822478a5125a40c18de84aa5acb5cf78070386448f528392481ae00437e1
|
|
BLAKE2b-256 checksum How to use checksums |
afb539711b9cd57fb7c68ffafd42048600da08754332ce693db60aa2061d9242
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log