commandagi 0.3.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- commandagi-0.5.0/.gitignore +8 -0
- commandagi-0.5.0/CHANGELOG.md +87 -0
- commandagi-0.5.0/LICENSE +21 -0
- commandagi-0.5.0/PKG-INFO +275 -0
- commandagi-0.5.0/README.md +247 -0
- commandagi-0.5.0/commandagi/__init__.py +63 -0
- commandagi-0.5.0/commandagi/_generated.py +663 -0
- commandagi-0.5.0/commandagi/client.py +276 -0
- commandagi-0.5.0/commandagi/design/__init__.py +115 -0
- commandagi-0.5.0/commandagi/design/business.py +211 -0
- commandagi-0.5.0/commandagi/design/cad.py +289 -0
- commandagi-0.5.0/commandagi/design/cadquery.py +289 -0
- commandagi-0.5.0/commandagi/design/documents.py +190 -0
- commandagi-0.5.0/commandagi/design/eda.py +226 -0
- commandagi-0.5.0/commandagi/design/element.py +163 -0
- commandagi-0.5.0/commandagi/design/fab.py +156 -0
- commandagi-0.5.0/commandagi/design/graph.py +41 -0
- commandagi-0.5.0/commandagi/design/ir.py +224 -0
- commandagi-0.5.0/commandagi/design/media.py +832 -0
- commandagi-0.5.0/commandagi/design/monitors.py +237 -0
- commandagi-0.5.0/commandagi/design/office.py +614 -0
- commandagi-0.5.0/commandagi/design/ontology.py +302 -0
- commandagi-0.5.0/commandagi/design/pcb.py +565 -0
- commandagi-0.5.0/commandagi/design/pdf.py +300 -0
- commandagi-0.5.0/commandagi/design/postal.py +111 -0
- commandagi-0.5.0/commandagi/design/records.py +87 -0
- commandagi-0.5.0/commandagi/design/schema.py +340 -0
- commandagi-0.5.0/commandagi/design/schematic.py +487 -0
- commandagi-0.5.0/commandagi/design/solids.py +153 -0
- commandagi-0.5.0/commandagi/design/source.py +366 -0
- commandagi-0.5.0/commandagi/design/standards.py +122 -0
- commandagi-0.5.0/commandagi/design/standards_data.py +12 -0
- commandagi-0.5.0/commandagi/design/tasks.py +122 -0
- commandagi-0.5.0/commandagi/design/threed.py +460 -0
- commandagi-0.5.0/commandagi/design/threed_elements.py +1272 -0
- commandagi-0.5.0/commandagi/design/threed_schema.py +4 -0
- commandagi-0.5.0/commandagi/design/twod.py +579 -0
- commandagi-0.5.0/commandagi/design/twod_schema.py +4 -0
- commandagi-0.5.0/commandagi/design/workbook_schema.py +4 -0
- {commandagi-0.3.0 → commandagi-0.5.0}/commandagi/gym_env.py +11 -11
- commandagi-0.5.0/commandagi/kernel.py +443 -0
- commandagi-0.5.0/commandagi-sdk.schema.json +2434 -0
- commandagi-0.5.0/examples/drive_a_sim.py +19 -0
- {commandagi-0.3.0 → commandagi-0.5.0}/pyproject.toml +5 -3
- commandagi-0.5.0/tests/test_business.py +72 -0
- commandagi-0.5.0/tests/test_design.py +161 -0
- commandagi-0.5.0/tests/test_fab.py +69 -0
- commandagi-0.5.0/tests/test_kernel.py +198 -0
- commandagi-0.5.0/tests/test_media.py +194 -0
- commandagi-0.5.0/tests/test_monitors.py +91 -0
- commandagi-0.5.0/tests/test_office.py +140 -0
- commandagi-0.5.0/tests/test_ontology.py +125 -0
- commandagi-0.5.0/tests/test_pcb.py +123 -0
- commandagi-0.5.0/tests/test_pdf.py +71 -0
- commandagi-0.5.0/tests/test_postal.py +63 -0
- commandagi-0.5.0/tests/test_records.py +59 -0
- commandagi-0.5.0/tests/test_schematic.py +148 -0
- commandagi-0.5.0/tests/test_sdk.py +145 -0
- commandagi-0.5.0/tests/test_source.py +114 -0
- commandagi-0.5.0/tests/test_standards.py +99 -0
- commandagi-0.5.0/tests/test_tasks.py +77 -0
- commandagi-0.5.0/tests/test_threed.py +253 -0
- commandagi-0.5.0/tests/test_twod.py +279 -0
- commandagi-0.5.0/uv.lock +1041 -0
- commandagi-0.3.0/.gitignore +0 -65
- commandagi-0.3.0/PKG-INFO +0 -159
- commandagi-0.3.0/README.md +0 -134
- commandagi-0.3.0/commandagi/__init__.py +0 -26
- commandagi-0.3.0/commandagi/agent.py +0 -172
- commandagi-0.3.0/commandagi/bridge.py +0 -133
- commandagi-0.3.0/commandagi/client.py +0 -504
- commandagi-0.3.0/examples/sim_agent.py +0 -44
- {commandagi-0.3.0 → commandagi-0.5.0}/commandagi/lerobot.py +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
One entry for each release. A release is the tag `v<version>` in this repository and the same version on PyPI. The
|
|
4
|
+
TypeScript and Rust SDKs carry the same version number. A version promises nothing about the next one (README
|
|
5
|
+
§ Versions): read this file before you change the version you pin.
|
|
6
|
+
|
|
7
|
+
## 0.5.0 (2026-10-14)
|
|
8
|
+
|
|
9
|
+
The first release with the design layer. 0.5.0 was prepared on 2026-10-13 and not published; this entry holds all of it.
|
|
10
|
+
|
|
11
|
+
Changed
|
|
12
|
+
|
|
13
|
+
- `Kernel` speaks the kernel contract (`POST <kernel>/v1/<op>`, `GET <kernel>/v1/health`) and is placed by
|
|
14
|
+
`COMMANDAGI_THREED`: `local`, `cloud`, both apart by `,` in the order to try (default `local,cloud`), or a running
|
|
15
|
+
service's URL. `platform` is not a value now: write `cloud`. A place where the kernel is not installed is passed
|
|
16
|
+
over; a refusal of the op is the answer. `Kernel.last_ran` says where each answer ran (`local` is never a cloud
|
|
17
|
+
result), and `Kernel.call(op, doc, params)` gives a whole answer. `observe` takes no `doc_id`.
|
|
18
|
+
|
|
19
|
+
Added
|
|
20
|
+
|
|
21
|
+
- A 3D document's engineering vocabulary (`commandagi.design.threed`, checked against the one schema, as in the
|
|
22
|
+
TypeScript SDK): `body(engineering={…})` (material grade, process, finish, vendor, cost, part number, mass),
|
|
23
|
+
`joint(…)` (`fixed`, `revolute`, `prismatic`, `cylindrical`, `planar`, `screw`, `spherical`, `universal`),
|
|
24
|
+
`mate(…)`, `sheet(…)` (an engineering drawing sheet), and `hole(size="M4 close fit")` (a hole by its standard name).
|
|
25
|
+
- `commandagi.design.standards`: `material`, `mass_grams`, `hole_size`, `sized_hole`, `standard_row`, `MATERIALS`,
|
|
26
|
+
`HOLE_SIZES`, `STANDARD_TABLES`, `STANDARD_SOURCES`: the same tables as the TypeScript SDK, each with where its
|
|
27
|
+
rows were read.
|
|
28
|
+
- `hole(body, at=…, size="M4 close fit", depth=…)` in the code-part primitives (`commandagi.design.cad`).
|
|
29
|
+
- `Kernel.sheet`, `Kernel.bom`, and `Kernel.observe(doc, materials=…)`: a body's `mass["grams"]`.
|
|
30
|
+
- 3D documents: a hole's `positions` and `sketch`; faces and edges chosen by a query, `{"kind", "select": {…}}`,
|
|
31
|
+
beside the pick; a part is built in the order of its elements (a feature that names no `consumes` follows every
|
|
32
|
+
body-making feature written before it).
|
|
33
|
+
- `Kernel.interference`, `Kernel.measure_all`, `linked_parts`; `parts=` on every call (an assembly's files);
|
|
34
|
+
`KernelError.failures`.
|
|
35
|
+
|
|
36
|
+
Changed
|
|
37
|
+
|
|
38
|
+
- A joint's kinds: `hinge` is `revolute`, `slider` is `prismatic`, `ball` is `spherical`. Joints and mates are
|
|
39
|
+
`joint(…)` and `mate(…)` elements: the reader refuses them inside `slot(name="dynamics")` and
|
|
40
|
+
`slot(name="assembly")`.
|
|
41
|
+
- `Kernel.measure(doc, quantity, a, b)` works with the platform too (the service takes `measures`).
|
|
42
|
+
- `Kernel.export`, `measure`, `drawing` and `tessellate` refuse a build in which a feature failed. `tessellate`
|
|
43
|
+
answers each body's triangles, not only their count.
|
|
44
|
+
- The call form (`part`, `subtract`, `hole`, `fillet` …) wires a feature to the heads of its body and to its last
|
|
45
|
+
feature: about two wires a feature, where a plate of 129 holes was 566 kB. `Body.state` is gone; `Body.heads` and
|
|
46
|
+
`Body.acts_on()` are what it has.
|
|
47
|
+
- A union's refusal names the field at fault (`edges/0/select/vertical: is not one of its fields`).
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
The first release that contains `commandagi.design`. PyPI 0.4.0 was built on 2026-09-23, before the design layer was
|
|
51
|
+
written; the version in the source stayed at 0.4.0 for the 106 commits after it, so the source and the package had
|
|
52
|
+
one number and different contents.
|
|
53
|
+
|
|
54
|
+
Added
|
|
55
|
+
|
|
56
|
+
- `commandagi.design`: documents declared in code, with the standard library only.
|
|
57
|
+
- The code-part primitives: CAD (`part`, `box`, `cylinder`, `sketch`, `extrude`, `subtract`, `hole`,
|
|
58
|
+
`linear_pattern` …), EDA (`circuit`, `board`, `component`, `net`, `connect`, `footprints`), any graph (`graph`,
|
|
59
|
+
`node`, `input_`, `code`), and `run_module`.
|
|
60
|
+
- One module for each document vocabulary: `schematic`, `pcb`, `threed`, `fab`, `office`, `twod`, `media`,
|
|
61
|
+
`ontology`, `business`, `tasks`, `monitors`, `records`, `postal`, `pdf`.
|
|
62
|
+
- `commandagi.design.cadquery`: a CadQuery-style importer.
|
|
63
|
+
- `commandagi.design.source`: the map from each element to the call that made it, for the editors' write-back.
|
|
64
|
+
- `commandagi.kernel.Kernel` (also `commandagi.Kernel`): `observe`, `export`, `measure`, `drawing` and `tessellate` of
|
|
65
|
+
a 3D document, by the CommandAGI 3D design service (OpenCascade). `COMMANDAGI_THREED` chooses the service:
|
|
66
|
+
`platform` (the default, with an API key), `local` (this computer, offline, no account: the `commandagi` npm
|
|
67
|
+
package's `commandagi threed serve`) or the URL of a running one.
|
|
68
|
+
- Client namespaces: `computer`, `billing`, `web`, `postal`, `social`, `phone`, `vpn`; agent constraints.
|
|
69
|
+
- `LICENSE` (MIT, as `pyproject.toml` states), this file, and § Versions in the README.
|
|
70
|
+
|
|
71
|
+
Changed
|
|
72
|
+
|
|
73
|
+
- The desktop control vocabulary has `mouse_down` and `mouse_up`.
|
|
74
|
+
|
|
75
|
+
Known limits
|
|
76
|
+
|
|
77
|
+
- `Kernel.measure` works with a local service only: the platform's route cannot be told which measurement.
|
|
78
|
+
- The hosted platform does not run a `.py` document's kernel calls for you; `Kernel` sends the graph that
|
|
79
|
+
`run_module` made.
|
|
80
|
+
|
|
81
|
+
## 0.4.0 (2026-09-23)
|
|
82
|
+
|
|
83
|
+
The generated client (`CommandAGI`, `Session` over the thread WebSocket) replaced `RobotBridge`.
|
|
84
|
+
|
|
85
|
+
## 0.3.0 (2026-09-05)
|
|
86
|
+
|
|
87
|
+
The first release by trusted publishing.
|
commandagi-0.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CommandAGI INC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: commandagi
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: The CommandAGI SDK for Python — agent threads, memory and integrations, and live computers, robots and simulated worlds you watch and control.
|
|
5
|
+
Project-URL: Homepage, https://commandagi.com
|
|
6
|
+
Project-URL: Documentation, https://github.com/CommandAGI/commandagi-sdk-python/blob/main/README.md
|
|
7
|
+
Project-URL: Repository, https://github.com/CommandAGI/commandagi-sdk-python
|
|
8
|
+
Project-URL: Changelog, https://github.com/CommandAGI/commandagi-sdk-python/blob/main/CHANGELOG.md
|
|
9
|
+
Author: CommandAGI
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,computer-use,physics,reinforcement-learning,robotics,simulation
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Requires-Dist: requests>=2.28
|
|
15
|
+
Requires-Dist: websocket-client>=1.5
|
|
16
|
+
Provides-Extra: gym
|
|
17
|
+
Requires-Dist: gymnasium>=0.29; extra == 'gym'
|
|
18
|
+
Requires-Dist: numpy>=1.23; extra == 'gym'
|
|
19
|
+
Requires-Dist: pillow>=9.0; extra == 'gym'
|
|
20
|
+
Provides-Extra: lerobot
|
|
21
|
+
Requires-Dist: numpy>=1.23; extra == 'lerobot'
|
|
22
|
+
Requires-Dist: pillow>=9.0; extra == 'lerobot'
|
|
23
|
+
Requires-Dist: pyarrow>=14.0; extra == 'lerobot'
|
|
24
|
+
Provides-Extra: vision
|
|
25
|
+
Requires-Dist: numpy>=1.23; extra == 'vision'
|
|
26
|
+
Requires-Dist: pillow>=9.0; extra == 'vision'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# commandagi — the CommandAGI Python SDK
|
|
30
|
+
|
|
31
|
+
Drive the whole CommandAGI platform as code: agent threads, memory and integrations, and live
|
|
32
|
+
**embodiments** (cloud computers, robots, simulated worlds) that you watch and control.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install commandagi
|
|
36
|
+
export COMMANDAGI_API_KEY=cagi_... # Settings → API keys
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from commandagi import CommandAGI
|
|
41
|
+
|
|
42
|
+
cagi = CommandAGI() # reads COMMANDAGI_API_KEY
|
|
43
|
+
|
|
44
|
+
# Agents: start one on a goal and follow along.
|
|
45
|
+
t = cagi.threads.create(intent="Summarise this week's robotics news in five bullets")
|
|
46
|
+
cagi.threads.send(t["threadId"], "Add a link for each one.")
|
|
47
|
+
|
|
48
|
+
# Embodiments: launch a world YOU drive (no agent in it), watch it, control it.
|
|
49
|
+
with cagi.launch("simulation/warehouse") as world:
|
|
50
|
+
print(world.controls()) # what it accepts right now, with payload schemas
|
|
51
|
+
world.sim.ik(target=[0.3, 0.0, 0.4]) # typed robot/sim control
|
|
52
|
+
jpeg = world.observe(fresh=True) # the next frame, after the move
|
|
53
|
+
# leaving the block stops the world and releases the machine
|
|
54
|
+
|
|
55
|
+
# Anything else: every platform tool by name.
|
|
56
|
+
cagi.call("list_snapshots")
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## One surface, generated
|
|
60
|
+
|
|
61
|
+
This SDK, the TypeScript SDK (`npm i commandagi`) and the `commandagi` CLI all come from **one
|
|
62
|
+
schema**, `commandagi-sdk.schema.json`, so they can't drift apart:
|
|
63
|
+
|
|
64
|
+
- **Tools**: `cagi.threads`, `cagi.embodiments`, `cagi.memory`, `cagi.integrations`,
|
|
65
|
+
`cagi.social(platform, account)`, `whoami`, `search`, `run` and `post`. Each one is a typed wrapper
|
|
66
|
+
over `call(tool, args)`. Options are passed as keyword arguments, e.g. `snapshot_id=…`, and sent
|
|
67
|
+
over the wire as camelCase (`snapshotId`). Where the schema names the options, they are typed
|
|
68
|
+
keyword-only arguments: `cagi.postal.send("Letters/Ada.letter.tsx", provider="click2mail")` (the
|
|
69
|
+
provider is `"lob"`, the default, or `"click2mail"`, US only; omitted, the account's
|
|
70
|
+
`postal.providers` order picks it).
|
|
71
|
+
- **Control vocabularies** on a live `Session`:
|
|
72
|
+
- `session.desktop` for computers: `click`, `double_click`, `move`, `scroll`, `type`, `key`, `wait`
|
|
73
|
+
- `session.robot` for physical robots: `joint`, `gripper`, `move`, `turn`, `home`, `stop`, …
|
|
74
|
+
- `session.sim` for simulated worlds: `reset`, `sim_mode`, `ctrl`, `actuator`, `ik`,
|
|
75
|
+
`trajectory`, `grab`, `force`, `add_robot`, …
|
|
76
|
+
|
|
77
|
+
Every method sends one action to that embodiment. The platform checks it against the controls the
|
|
78
|
+
embodiment is currently declaring, which `session.controls()` lists along with their payload
|
|
79
|
+
schemas. For anything a runtime declares that no vocabulary covers, use `session.act(action,
|
|
80
|
+
payload)`.
|
|
81
|
+
|
|
82
|
+
The generated part is `commandagi/_generated.py`. Don't edit it: it is regenerated from the schema in
|
|
83
|
+
the CommandAGI monorepo.
|
|
84
|
+
|
|
85
|
+
## Sessions
|
|
86
|
+
|
|
87
|
+
| | |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `cagi.launch(snapshot_id, title=, wait=True, timeout=600)` | a new agentless thread running that snapshot; waits until it declares controls |
|
|
90
|
+
| `cagi.session(thread_id, embodiment_id)` | attach to an embodiment that already exists |
|
|
91
|
+
| `session.frame(fresh=False)` / `observe()` / `observe_array()` / `frames()` | the latest frame, as `{url, channel_id, at}`, image bytes, or a numpy array (`pip install "commandagi[vision]"`); `frames()` yields each new one |
|
|
92
|
+
| `session.describe()` | the live world a sim or robot runtime reports |
|
|
93
|
+
| `session.close()` / `stop()` | stop watching / also stop a world this session launched |
|
|
94
|
+
|
|
95
|
+
Snapshot ids come from `cagi.call("list_snapshots")`, e.g. `simulation/warehouse` or
|
|
96
|
+
`computer/software-engineer`.
|
|
97
|
+
|
|
98
|
+
## Design in code: `commandagi.design`
|
|
99
|
+
|
|
100
|
+
The same structure-declaring primitives as the TypeScript SDK's `commandagi/design`, producing the same op graph
|
|
101
|
+
(plain JSON). `commandagi.design` itself holds the code-part primitives: CAD (`part`, `box`, `cylinder`, `sketch`,
|
|
102
|
+
`extrude`, `subtract`, `hole`, `linear_pattern` and the rest), EDA (`circuit`, `board`, `component`, `net`, `connect`,
|
|
103
|
+
`footprints`), any graph (`graph`, `node`, `input_`, `code`) and `run_module`. There is no kernel, solver, router or
|
|
104
|
+
renderer; importing `commandagi.design` needs nothing but the standard library.
|
|
105
|
+
|
|
106
|
+
### Documents: one module per vocabulary
|
|
107
|
+
|
|
108
|
+
Every document a TypeScript file writes in JSX, a Python file writes as calls, element for element. One module per
|
|
109
|
+
vocabulary, one function per tag, named by the tag:
|
|
110
|
+
|
|
111
|
+
| module | documents |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `commandagi.design.schematic` | a schematic (`group`) |
|
|
114
|
+
| `commandagi.design.pcb` | a board in code (`board`) |
|
|
115
|
+
| `commandagi.design.threed` | a 3D part or assembly |
|
|
116
|
+
| `commandagi.design.fab` | a machining setup (`cam`), a slicing setup (`slicing`) |
|
|
117
|
+
| `commandagi.design.office` | a workbook, a page, a deck |
|
|
118
|
+
| `commandagi.design.twod` | a drawing, a painting, a photo, a nest |
|
|
119
|
+
| `commandagi.design.media` | a video (clips, `composition(src="Intro.vid.py")`: another video drawn live from its own code, `artboard(src="Poster.draw.py")`: a drawing's page drawn live), a song |
|
|
120
|
+
| `commandagi.design.ontology` | a world, a device definition, a dashboard, a geo project, a node graph |
|
|
121
|
+
| `commandagi.design.business` | a company, an RFC, a case |
|
|
122
|
+
| `commandagi.design.tasks` | a task, a project |
|
|
123
|
+
| `commandagi.design.monitors` | a monitor (`monitor.py`, in the folder of the thing it follows) |
|
|
124
|
+
| `commandagi.design.records` | a contract, a product instance |
|
|
125
|
+
| `commandagi.design.postal` | a letter, a postcard |
|
|
126
|
+
| `commandagi.design.pdf` | a PDF assembled from pages, comments, stamps, signatures, redactions, fields and bookmarks |
|
|
127
|
+
|
|
128
|
+
The rules of a call, the same in every module:
|
|
129
|
+
|
|
130
|
+
- `tag(*children, **attributes)`. The function's name is the tag: `-` becomes `_` (`brush_stroke`), a Python keyword
|
|
131
|
+
gets a trailing `_` (`from_`), the case is kept (`Company`).
|
|
132
|
+
- Children are positional, in order. A tag that holds text takes texts and inline elements:
|
|
133
|
+
`p("The run ", b("passed"), ".")`. A loop is a starred argument: `layer(*[rect(x=i * 10) for i in range(3)])`.
|
|
134
|
+
A leaf takes no children.
|
|
135
|
+
- Attributes are keywords in snake_case of the TypeScript name: `frozen_rows=1` is `frozenRows`, `sch_x=114.3` is
|
|
136
|
+
`schX`. A keyword with an uppercase letter is refused by name.
|
|
137
|
+
- Only the schematic has positional attributes: `resistor("R1", …)`, `group("Divider", …)`, `trace(from, to)`,
|
|
138
|
+
`netlabel(net, connection)`, `unit(part, unit)`.
|
|
139
|
+
- The file's result is `result = <root>(…)` (or `main()`, or `show_object`).
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from commandagi.design.twod import drawing, ellipse, group, layer, rect
|
|
143
|
+
|
|
144
|
+
result = drawing(
|
|
145
|
+
layer(
|
|
146
|
+
rect(x=40, y=40, w=200, h=120, fill="#3b82f6"),
|
|
147
|
+
group(ellipse(cx=500, cy=300, rx=60, ry=60, fill="#f59e0b"), name="Badge"),
|
|
148
|
+
name="Layer 1",
|
|
149
|
+
),
|
|
150
|
+
name="Poster", width=800, height=600, background="#ffffff",
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from commandagi.design.schematic import ground, group, netlabel, resistor, trace, voltagesource
|
|
156
|
+
|
|
157
|
+
result = group(
|
|
158
|
+
"Divider",
|
|
159
|
+
voltagesource("V1", voltage="9", sch_x=114.3, sch_y=114.3),
|
|
160
|
+
resistor("R1", resistance="3k", sch_x=114.3, sch_y=88.9, sch_rotation=90),
|
|
161
|
+
ground("#PWR1", sch_x=139.7, sch_y=114.3),
|
|
162
|
+
trace(".V1 > .pos", ".R1 > .pin1"),
|
|
163
|
+
netlabel("OUT", ".R1 > .pin2"),
|
|
164
|
+
)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
A graph document (a schematic, a board, a 3D part, a drawing, a deck, a video, a node graph) leaves `run_module` as
|
|
168
|
+
its graph. Any other document (a workbook, a world, a company, a task …) leaves as `document` (`{format, document,
|
|
169
|
+
sources}`) beside an empty graph.
|
|
170
|
+
|
|
171
|
+
### Where each element is written
|
|
172
|
+
|
|
173
|
+
`run_module` maps the file's calls with Python's `ast` (`commandagi.design.source`). Each element records the call
|
|
174
|
+
that made it. A declaration puts that call where the TypeScript SDK puts a JSX element's source: a node's
|
|
175
|
+
`meta.source`, a node's `meta.sources` by key, a document's `sources` by key. Each one holds the call's span, each
|
|
176
|
+
keyword's span with its literal value or its expression, and how many times the call ran. The whole map rides on the
|
|
177
|
+
graph as `meta.sourceMap`: every call with its arguments, its parent element, its slot among its parent's children,
|
|
178
|
+
and whether it runs in a loop. The editor writes each edit back into the file as the smallest text edit. A keyword
|
|
179
|
+
that is an expression is never replaced with a literal: the edit is refused with its line.
|
|
180
|
+
|
|
181
|
+
### Code parts and CadQuery
|
|
182
|
+
|
|
183
|
+
`commandagi.design.cadquery` is a CadQuery-style importer: `Workplane("XY").box(…).faces(">Z").workplane()
|
|
184
|
+
.rarray(…).hole(…)` records the same features the primitives declare. It refuses, by name, what needs real topology
|
|
185
|
+
(fillets, edge selectors).
|
|
186
|
+
|
|
187
|
+
A `.py` code part is a script. Its result is `main(**inputs)` if it defines `main`; otherwise what it passed to
|
|
188
|
+
`show_object`; otherwise its `result` variable. `param(name, default, unit=…)` declares an input and reads its value.
|
|
189
|
+
In a CommandAGI editor, a `.py` code node runs under Pyodide in a sandboxed worker, where `import cadquery as cq` is
|
|
190
|
+
this importer (OCCT does not run there). `run_module(source, path, inputs)` is what the sandbox calls. Tests:
|
|
191
|
+
`python -m unittest discover -s tests`.
|
|
192
|
+
|
|
193
|
+
## The 3D kernel: `commandagi.kernel`
|
|
194
|
+
|
|
195
|
+
`commandagi.design.threed` declares a part and builds nothing. `Kernel` asks the CommandAGI 3D design service
|
|
196
|
+
(OpenCascade) to build it. It uses the standard library only.
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from commandagi.design.threed import box, cylinder, part
|
|
200
|
+
from commandagi.kernel import Kernel
|
|
201
|
+
|
|
202
|
+
plate = part(
|
|
203
|
+
box(id="plate", center=[0, 0, 4], size=[60, 40, 8], operation="new"),
|
|
204
|
+
cylinder(id="hole", center=[0, 0, -1], axis=[0, 0, 1], radius=3, height=10, operation="cut"),
|
|
205
|
+
name="Plate",
|
|
206
|
+
)
|
|
207
|
+
kernel = Kernel() # reads COMMANDAGI_THREED
|
|
208
|
+
seen = kernel.observe(plate)["observation"] # bodies, faces, edges, mass properties; status and failures
|
|
209
|
+
assert seen["status"] == "built", seen["failures"] # "failed": the bodies are built from the features that did not fail
|
|
210
|
+
open("plate.step", "w").write(kernel.export(plate, "step")) # refused (KernelError.failures) when a feature failed
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
A part is built in the order of its elements: the cut above follows the plate because it is written after it.
|
|
214
|
+
`consumes=[…]` names the features a feature follows instead.
|
|
215
|
+
|
|
216
|
+
One setting says where it runs: `COMMANDAGI_THREED`, the places to try in order, apart by `,` (default `local,cloud`).
|
|
217
|
+
A place where the kernel is not installed is passed over. A refusal of the op itself (a build that is not whole) is
|
|
218
|
+
the answer: it is not asked of the next place. `kernel.last_ran` says where each answer ran (`{"kernel", "build",
|
|
219
|
+
"place"}`); `place` is where the SDK sent the request, so a result made on this computer says `local` and is never a
|
|
220
|
+
cloud result.
|
|
221
|
+
|
|
222
|
+
| place | the kernel | needs |
|
|
223
|
+
| --- | --- | --- |
|
|
224
|
+
| `local` | this computer, offline, no account. `Kernel` starts it and stops it when Python exits; `COMMANDAGI_THREED_URL` names one that runs already (`commandagi threed serve --port 8091`) | Node 22 and, once: `npm install -g commandagi` then `commandagi threed install` (it downloads OpenCascade as WebAssembly, about 130 MB); for a running one, `COMMANDAGI_THREED_TOKEN`, the bearer you started it with |
|
|
225
|
+
| `cloud` | the platform's kernel door (`/kernels/3d/v1/<op>`) | the network and `COMMANDAGI_API_KEY` |
|
|
226
|
+
|
|
227
|
+
A URL in `COMMANDAGI_THREED` is that running service only. Both places speak one contract (`POST <kernel>/v1/<op>`,
|
|
228
|
+
`GET <kernel>/v1/health`), and `kernel.call(op, doc, params)` gives a whole answer (`outputs`, `result`, `ran`).
|
|
229
|
+
|
|
230
|
+
`observe(doc)`, `export(doc, "step" | "iges", bodies=)`, `measure(doc, "distance" | "angle" | "radius" | "closest", a,
|
|
231
|
+
b)`, `measure_all(doc, [{"quantity", "a", "b"}, …])` (many on one build), `interference(doc, clearance=, bodies=)`,
|
|
232
|
+
`drawing(doc, direction=, up=, section=)`, `tessellate(doc, deflection)`. `doc` is what a file declares, its
|
|
233
|
+
graph (`run_module(...)["graph"]`) or the graph's JSON text. An entity is `{"body": "body_0", "kind": "face" | "edge" |
|
|
234
|
+
"vertex" | "body", "index": n}`, with the index as `observe` lists it.
|
|
235
|
+
|
|
236
|
+
- **A build in which a feature failed is not whole.** `observe` answers it with `status: "failed"` and `failures`.
|
|
237
|
+
Every other call refuses it: `KernelError` with `status` 422 and `failures` (each failed feature, with why).
|
|
238
|
+
- **`interference`** compares every pair of bodies: `clashes` (shared volume), `engagements` (shared volume that all
|
|
239
|
+
lies in a declared `thread`: a screw in a tapped hole) and, with `clearance`, `close` (nearer than it). Each body
|
|
240
|
+
has its part's name.
|
|
241
|
+
- **An assembly's files** go with the call: `parts=linked_parts({"bolt.3d.py": bolt, "bracket.step": text})`, each
|
|
242
|
+
under the name its `externalPart` gives.
|
|
243
|
+
- **A STEP file names its parts**: each body is a product of its part's name, an assembly the product they are
|
|
244
|
+
components of.
|
|
245
|
+
|
|
246
|
+
## Reinforcement learning
|
|
247
|
+
|
|
248
|
+
`commandagi.gym_env.CommandAGIEnv` wraps a sim session as a Gymnasium env (`pip install
|
|
249
|
+
"commandagi[gym]"`), and `commandagi.lerobot` records rollouts as LeRobot datasets (`pip install
|
|
250
|
+
"commandagi[lerobot]"`).
|
|
251
|
+
|
|
252
|
+
## Environment
|
|
253
|
+
|
|
254
|
+
| variable | meaning |
|
|
255
|
+
| --- | --- |
|
|
256
|
+
| `COMMANDAGI_API_KEY` | your `cagi_…` key. Its scopes decide what every call may do |
|
|
257
|
+
| `COMMANDAGI_BASE_URL` | API origin (default `https://api.commandagi.com`) |
|
|
258
|
+
| `COMMANDAGI_THREAD_ID` | default thread for self-thread methods. Set automatically when your code runs inside the platform |
|
|
259
|
+
| `COMMANDAGI_THREED` | where `Kernel` runs: `local`, `cloud`, both apart by `,` in the order to try (default `local,cloud`), or a running service's URL |
|
|
260
|
+
| `COMMANDAGI_THREED_URL`, `COMMANDAGI_THREED_TOKEN` | a 3D design service that runs already, and its bearer |
|
|
261
|
+
|
|
262
|
+
## Versions
|
|
263
|
+
|
|
264
|
+
- A release is the tag `v<version>` in this repository and the same version on PyPI. Pin an exact version:
|
|
265
|
+
`commandagi==0.5.0`. A range (`^`, `~=`, `>=`) takes the next release without asking you.
|
|
266
|
+
- The three SDKs (Python, TypeScript, Rust) carry one version number. A vocabulary at version X in one language is
|
|
267
|
+
the vocabulary at version X in the others.
|
|
268
|
+
- A version promises nothing about the next one. CommandAGI keeps no compatibility layers: a release may rename or
|
|
269
|
+
remove a function, an element or an attribute, with no alias and no migration. `CHANGELOG.md` says what changed.
|
|
270
|
+
- So a document belongs with the version that reads it. Keep the pin in the project that holds the documents, and
|
|
271
|
+
change both together. A document you open in a CommandAGI editor is read by the SDK that editor ships.
|
|
272
|
+
|
|
273
|
+
## Licence
|
|
274
|
+
|
|
275
|
+
MIT (`LICENSE`).
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# commandagi — the CommandAGI Python SDK
|
|
2
|
+
|
|
3
|
+
Drive the whole CommandAGI platform as code: agent threads, memory and integrations, and live
|
|
4
|
+
**embodiments** (cloud computers, robots, simulated worlds) that you watch and control.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install commandagi
|
|
8
|
+
export COMMANDAGI_API_KEY=cagi_... # Settings → API keys
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from commandagi import CommandAGI
|
|
13
|
+
|
|
14
|
+
cagi = CommandAGI() # reads COMMANDAGI_API_KEY
|
|
15
|
+
|
|
16
|
+
# Agents: start one on a goal and follow along.
|
|
17
|
+
t = cagi.threads.create(intent="Summarise this week's robotics news in five bullets")
|
|
18
|
+
cagi.threads.send(t["threadId"], "Add a link for each one.")
|
|
19
|
+
|
|
20
|
+
# Embodiments: launch a world YOU drive (no agent in it), watch it, control it.
|
|
21
|
+
with cagi.launch("simulation/warehouse") as world:
|
|
22
|
+
print(world.controls()) # what it accepts right now, with payload schemas
|
|
23
|
+
world.sim.ik(target=[0.3, 0.0, 0.4]) # typed robot/sim control
|
|
24
|
+
jpeg = world.observe(fresh=True) # the next frame, after the move
|
|
25
|
+
# leaving the block stops the world and releases the machine
|
|
26
|
+
|
|
27
|
+
# Anything else: every platform tool by name.
|
|
28
|
+
cagi.call("list_snapshots")
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## One surface, generated
|
|
32
|
+
|
|
33
|
+
This SDK, the TypeScript SDK (`npm i commandagi`) and the `commandagi` CLI all come from **one
|
|
34
|
+
schema**, `commandagi-sdk.schema.json`, so they can't drift apart:
|
|
35
|
+
|
|
36
|
+
- **Tools**: `cagi.threads`, `cagi.embodiments`, `cagi.memory`, `cagi.integrations`,
|
|
37
|
+
`cagi.social(platform, account)`, `whoami`, `search`, `run` and `post`. Each one is a typed wrapper
|
|
38
|
+
over `call(tool, args)`. Options are passed as keyword arguments, e.g. `snapshot_id=…`, and sent
|
|
39
|
+
over the wire as camelCase (`snapshotId`). Where the schema names the options, they are typed
|
|
40
|
+
keyword-only arguments: `cagi.postal.send("Letters/Ada.letter.tsx", provider="click2mail")` (the
|
|
41
|
+
provider is `"lob"`, the default, or `"click2mail"`, US only; omitted, the account's
|
|
42
|
+
`postal.providers` order picks it).
|
|
43
|
+
- **Control vocabularies** on a live `Session`:
|
|
44
|
+
- `session.desktop` for computers: `click`, `double_click`, `move`, `scroll`, `type`, `key`, `wait`
|
|
45
|
+
- `session.robot` for physical robots: `joint`, `gripper`, `move`, `turn`, `home`, `stop`, …
|
|
46
|
+
- `session.sim` for simulated worlds: `reset`, `sim_mode`, `ctrl`, `actuator`, `ik`,
|
|
47
|
+
`trajectory`, `grab`, `force`, `add_robot`, …
|
|
48
|
+
|
|
49
|
+
Every method sends one action to that embodiment. The platform checks it against the controls the
|
|
50
|
+
embodiment is currently declaring, which `session.controls()` lists along with their payload
|
|
51
|
+
schemas. For anything a runtime declares that no vocabulary covers, use `session.act(action,
|
|
52
|
+
payload)`.
|
|
53
|
+
|
|
54
|
+
The generated part is `commandagi/_generated.py`. Don't edit it: it is regenerated from the schema in
|
|
55
|
+
the CommandAGI monorepo.
|
|
56
|
+
|
|
57
|
+
## Sessions
|
|
58
|
+
|
|
59
|
+
| | |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `cagi.launch(snapshot_id, title=, wait=True, timeout=600)` | a new agentless thread running that snapshot; waits until it declares controls |
|
|
62
|
+
| `cagi.session(thread_id, embodiment_id)` | attach to an embodiment that already exists |
|
|
63
|
+
| `session.frame(fresh=False)` / `observe()` / `observe_array()` / `frames()` | the latest frame, as `{url, channel_id, at}`, image bytes, or a numpy array (`pip install "commandagi[vision]"`); `frames()` yields each new one |
|
|
64
|
+
| `session.describe()` | the live world a sim or robot runtime reports |
|
|
65
|
+
| `session.close()` / `stop()` | stop watching / also stop a world this session launched |
|
|
66
|
+
|
|
67
|
+
Snapshot ids come from `cagi.call("list_snapshots")`, e.g. `simulation/warehouse` or
|
|
68
|
+
`computer/software-engineer`.
|
|
69
|
+
|
|
70
|
+
## Design in code: `commandagi.design`
|
|
71
|
+
|
|
72
|
+
The same structure-declaring primitives as the TypeScript SDK's `commandagi/design`, producing the same op graph
|
|
73
|
+
(plain JSON). `commandagi.design` itself holds the code-part primitives: CAD (`part`, `box`, `cylinder`, `sketch`,
|
|
74
|
+
`extrude`, `subtract`, `hole`, `linear_pattern` and the rest), EDA (`circuit`, `board`, `component`, `net`, `connect`,
|
|
75
|
+
`footprints`), any graph (`graph`, `node`, `input_`, `code`) and `run_module`. There is no kernel, solver, router or
|
|
76
|
+
renderer; importing `commandagi.design` needs nothing but the standard library.
|
|
77
|
+
|
|
78
|
+
### Documents: one module per vocabulary
|
|
79
|
+
|
|
80
|
+
Every document a TypeScript file writes in JSX, a Python file writes as calls, element for element. One module per
|
|
81
|
+
vocabulary, one function per tag, named by the tag:
|
|
82
|
+
|
|
83
|
+
| module | documents |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| `commandagi.design.schematic` | a schematic (`group`) |
|
|
86
|
+
| `commandagi.design.pcb` | a board in code (`board`) |
|
|
87
|
+
| `commandagi.design.threed` | a 3D part or assembly |
|
|
88
|
+
| `commandagi.design.fab` | a machining setup (`cam`), a slicing setup (`slicing`) |
|
|
89
|
+
| `commandagi.design.office` | a workbook, a page, a deck |
|
|
90
|
+
| `commandagi.design.twod` | a drawing, a painting, a photo, a nest |
|
|
91
|
+
| `commandagi.design.media` | a video (clips, `composition(src="Intro.vid.py")`: another video drawn live from its own code, `artboard(src="Poster.draw.py")`: a drawing's page drawn live), a song |
|
|
92
|
+
| `commandagi.design.ontology` | a world, a device definition, a dashboard, a geo project, a node graph |
|
|
93
|
+
| `commandagi.design.business` | a company, an RFC, a case |
|
|
94
|
+
| `commandagi.design.tasks` | a task, a project |
|
|
95
|
+
| `commandagi.design.monitors` | a monitor (`monitor.py`, in the folder of the thing it follows) |
|
|
96
|
+
| `commandagi.design.records` | a contract, a product instance |
|
|
97
|
+
| `commandagi.design.postal` | a letter, a postcard |
|
|
98
|
+
| `commandagi.design.pdf` | a PDF assembled from pages, comments, stamps, signatures, redactions, fields and bookmarks |
|
|
99
|
+
|
|
100
|
+
The rules of a call, the same in every module:
|
|
101
|
+
|
|
102
|
+
- `tag(*children, **attributes)`. The function's name is the tag: `-` becomes `_` (`brush_stroke`), a Python keyword
|
|
103
|
+
gets a trailing `_` (`from_`), the case is kept (`Company`).
|
|
104
|
+
- Children are positional, in order. A tag that holds text takes texts and inline elements:
|
|
105
|
+
`p("The run ", b("passed"), ".")`. A loop is a starred argument: `layer(*[rect(x=i * 10) for i in range(3)])`.
|
|
106
|
+
A leaf takes no children.
|
|
107
|
+
- Attributes are keywords in snake_case of the TypeScript name: `frozen_rows=1` is `frozenRows`, `sch_x=114.3` is
|
|
108
|
+
`schX`. A keyword with an uppercase letter is refused by name.
|
|
109
|
+
- Only the schematic has positional attributes: `resistor("R1", …)`, `group("Divider", …)`, `trace(from, to)`,
|
|
110
|
+
`netlabel(net, connection)`, `unit(part, unit)`.
|
|
111
|
+
- The file's result is `result = <root>(…)` (or `main()`, or `show_object`).
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from commandagi.design.twod import drawing, ellipse, group, layer, rect
|
|
115
|
+
|
|
116
|
+
result = drawing(
|
|
117
|
+
layer(
|
|
118
|
+
rect(x=40, y=40, w=200, h=120, fill="#3b82f6"),
|
|
119
|
+
group(ellipse(cx=500, cy=300, rx=60, ry=60, fill="#f59e0b"), name="Badge"),
|
|
120
|
+
name="Layer 1",
|
|
121
|
+
),
|
|
122
|
+
name="Poster", width=800, height=600, background="#ffffff",
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from commandagi.design.schematic import ground, group, netlabel, resistor, trace, voltagesource
|
|
128
|
+
|
|
129
|
+
result = group(
|
|
130
|
+
"Divider",
|
|
131
|
+
voltagesource("V1", voltage="9", sch_x=114.3, sch_y=114.3),
|
|
132
|
+
resistor("R1", resistance="3k", sch_x=114.3, sch_y=88.9, sch_rotation=90),
|
|
133
|
+
ground("#PWR1", sch_x=139.7, sch_y=114.3),
|
|
134
|
+
trace(".V1 > .pos", ".R1 > .pin1"),
|
|
135
|
+
netlabel("OUT", ".R1 > .pin2"),
|
|
136
|
+
)
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
A graph document (a schematic, a board, a 3D part, a drawing, a deck, a video, a node graph) leaves `run_module` as
|
|
140
|
+
its graph. Any other document (a workbook, a world, a company, a task …) leaves as `document` (`{format, document,
|
|
141
|
+
sources}`) beside an empty graph.
|
|
142
|
+
|
|
143
|
+
### Where each element is written
|
|
144
|
+
|
|
145
|
+
`run_module` maps the file's calls with Python's `ast` (`commandagi.design.source`). Each element records the call
|
|
146
|
+
that made it. A declaration puts that call where the TypeScript SDK puts a JSX element's source: a node's
|
|
147
|
+
`meta.source`, a node's `meta.sources` by key, a document's `sources` by key. Each one holds the call's span, each
|
|
148
|
+
keyword's span with its literal value or its expression, and how many times the call ran. The whole map rides on the
|
|
149
|
+
graph as `meta.sourceMap`: every call with its arguments, its parent element, its slot among its parent's children,
|
|
150
|
+
and whether it runs in a loop. The editor writes each edit back into the file as the smallest text edit. A keyword
|
|
151
|
+
that is an expression is never replaced with a literal: the edit is refused with its line.
|
|
152
|
+
|
|
153
|
+
### Code parts and CadQuery
|
|
154
|
+
|
|
155
|
+
`commandagi.design.cadquery` is a CadQuery-style importer: `Workplane("XY").box(…).faces(">Z").workplane()
|
|
156
|
+
.rarray(…).hole(…)` records the same features the primitives declare. It refuses, by name, what needs real topology
|
|
157
|
+
(fillets, edge selectors).
|
|
158
|
+
|
|
159
|
+
A `.py` code part is a script. Its result is `main(**inputs)` if it defines `main`; otherwise what it passed to
|
|
160
|
+
`show_object`; otherwise its `result` variable. `param(name, default, unit=…)` declares an input and reads its value.
|
|
161
|
+
In a CommandAGI editor, a `.py` code node runs under Pyodide in a sandboxed worker, where `import cadquery as cq` is
|
|
162
|
+
this importer (OCCT does not run there). `run_module(source, path, inputs)` is what the sandbox calls. Tests:
|
|
163
|
+
`python -m unittest discover -s tests`.
|
|
164
|
+
|
|
165
|
+
## The 3D kernel: `commandagi.kernel`
|
|
166
|
+
|
|
167
|
+
`commandagi.design.threed` declares a part and builds nothing. `Kernel` asks the CommandAGI 3D design service
|
|
168
|
+
(OpenCascade) to build it. It uses the standard library only.
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from commandagi.design.threed import box, cylinder, part
|
|
172
|
+
from commandagi.kernel import Kernel
|
|
173
|
+
|
|
174
|
+
plate = part(
|
|
175
|
+
box(id="plate", center=[0, 0, 4], size=[60, 40, 8], operation="new"),
|
|
176
|
+
cylinder(id="hole", center=[0, 0, -1], axis=[0, 0, 1], radius=3, height=10, operation="cut"),
|
|
177
|
+
name="Plate",
|
|
178
|
+
)
|
|
179
|
+
kernel = Kernel() # reads COMMANDAGI_THREED
|
|
180
|
+
seen = kernel.observe(plate)["observation"] # bodies, faces, edges, mass properties; status and failures
|
|
181
|
+
assert seen["status"] == "built", seen["failures"] # "failed": the bodies are built from the features that did not fail
|
|
182
|
+
open("plate.step", "w").write(kernel.export(plate, "step")) # refused (KernelError.failures) when a feature failed
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
A part is built in the order of its elements: the cut above follows the plate because it is written after it.
|
|
186
|
+
`consumes=[…]` names the features a feature follows instead.
|
|
187
|
+
|
|
188
|
+
One setting says where it runs: `COMMANDAGI_THREED`, the places to try in order, apart by `,` (default `local,cloud`).
|
|
189
|
+
A place where the kernel is not installed is passed over. A refusal of the op itself (a build that is not whole) is
|
|
190
|
+
the answer: it is not asked of the next place. `kernel.last_ran` says where each answer ran (`{"kernel", "build",
|
|
191
|
+
"place"}`); `place` is where the SDK sent the request, so a result made on this computer says `local` and is never a
|
|
192
|
+
cloud result.
|
|
193
|
+
|
|
194
|
+
| place | the kernel | needs |
|
|
195
|
+
| --- | --- | --- |
|
|
196
|
+
| `local` | this computer, offline, no account. `Kernel` starts it and stops it when Python exits; `COMMANDAGI_THREED_URL` names one that runs already (`commandagi threed serve --port 8091`) | Node 22 and, once: `npm install -g commandagi` then `commandagi threed install` (it downloads OpenCascade as WebAssembly, about 130 MB); for a running one, `COMMANDAGI_THREED_TOKEN`, the bearer you started it with |
|
|
197
|
+
| `cloud` | the platform's kernel door (`/kernels/3d/v1/<op>`) | the network and `COMMANDAGI_API_KEY` |
|
|
198
|
+
|
|
199
|
+
A URL in `COMMANDAGI_THREED` is that running service only. Both places speak one contract (`POST <kernel>/v1/<op>`,
|
|
200
|
+
`GET <kernel>/v1/health`), and `kernel.call(op, doc, params)` gives a whole answer (`outputs`, `result`, `ran`).
|
|
201
|
+
|
|
202
|
+
`observe(doc)`, `export(doc, "step" | "iges", bodies=)`, `measure(doc, "distance" | "angle" | "radius" | "closest", a,
|
|
203
|
+
b)`, `measure_all(doc, [{"quantity", "a", "b"}, …])` (many on one build), `interference(doc, clearance=, bodies=)`,
|
|
204
|
+
`drawing(doc, direction=, up=, section=)`, `tessellate(doc, deflection)`. `doc` is what a file declares, its
|
|
205
|
+
graph (`run_module(...)["graph"]`) or the graph's JSON text. An entity is `{"body": "body_0", "kind": "face" | "edge" |
|
|
206
|
+
"vertex" | "body", "index": n}`, with the index as `observe` lists it.
|
|
207
|
+
|
|
208
|
+
- **A build in which a feature failed is not whole.** `observe` answers it with `status: "failed"` and `failures`.
|
|
209
|
+
Every other call refuses it: `KernelError` with `status` 422 and `failures` (each failed feature, with why).
|
|
210
|
+
- **`interference`** compares every pair of bodies: `clashes` (shared volume), `engagements` (shared volume that all
|
|
211
|
+
lies in a declared `thread`: a screw in a tapped hole) and, with `clearance`, `close` (nearer than it). Each body
|
|
212
|
+
has its part's name.
|
|
213
|
+
- **An assembly's files** go with the call: `parts=linked_parts({"bolt.3d.py": bolt, "bracket.step": text})`, each
|
|
214
|
+
under the name its `externalPart` gives.
|
|
215
|
+
- **A STEP file names its parts**: each body is a product of its part's name, an assembly the product they are
|
|
216
|
+
components of.
|
|
217
|
+
|
|
218
|
+
## Reinforcement learning
|
|
219
|
+
|
|
220
|
+
`commandagi.gym_env.CommandAGIEnv` wraps a sim session as a Gymnasium env (`pip install
|
|
221
|
+
"commandagi[gym]"`), and `commandagi.lerobot` records rollouts as LeRobot datasets (`pip install
|
|
222
|
+
"commandagi[lerobot]"`).
|
|
223
|
+
|
|
224
|
+
## Environment
|
|
225
|
+
|
|
226
|
+
| variable | meaning |
|
|
227
|
+
| --- | --- |
|
|
228
|
+
| `COMMANDAGI_API_KEY` | your `cagi_…` key. Its scopes decide what every call may do |
|
|
229
|
+
| `COMMANDAGI_BASE_URL` | API origin (default `https://api.commandagi.com`) |
|
|
230
|
+
| `COMMANDAGI_THREAD_ID` | default thread for self-thread methods. Set automatically when your code runs inside the platform |
|
|
231
|
+
| `COMMANDAGI_THREED` | where `Kernel` runs: `local`, `cloud`, both apart by `,` in the order to try (default `local,cloud`), or a running service's URL |
|
|
232
|
+
| `COMMANDAGI_THREED_URL`, `COMMANDAGI_THREED_TOKEN` | a 3D design service that runs already, and its bearer |
|
|
233
|
+
|
|
234
|
+
## Versions
|
|
235
|
+
|
|
236
|
+
- A release is the tag `v<version>` in this repository and the same version on PyPI. Pin an exact version:
|
|
237
|
+
`commandagi==0.5.0`. A range (`^`, `~=`, `>=`) takes the next release without asking you.
|
|
238
|
+
- The three SDKs (Python, TypeScript, Rust) carry one version number. A vocabulary at version X in one language is
|
|
239
|
+
the vocabulary at version X in the others.
|
|
240
|
+
- A version promises nothing about the next one. CommandAGI keeps no compatibility layers: a release may rename or
|
|
241
|
+
remove a function, an element or an attribute, with no alias and no migration. `CHANGELOG.md` says what changed.
|
|
242
|
+
- So a document belongs with the version that reads it. Keep the pin in the project that holds the documents, and
|
|
243
|
+
change both together. A document you open in a CommandAGI editor is read by the SDK that editor ships.
|
|
244
|
+
|
|
245
|
+
## Licence
|
|
246
|
+
|
|
247
|
+
MIT (`LICENSE`).
|