bowerbot 1.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.
Files changed (57) hide show
  1. bowerbot-1.5.0/PKG-INFO +597 -0
  2. bowerbot-1.5.0/README.md +569 -0
  3. bowerbot-1.5.0/pyproject.toml +61 -0
  4. bowerbot-1.5.0/src/bowerbot/__init__.py +8 -0
  5. bowerbot-1.5.0/src/bowerbot/agent.py +185 -0
  6. bowerbot-1.5.0/src/bowerbot/cli.py +430 -0
  7. bowerbot-1.5.0/src/bowerbot/config.py +141 -0
  8. bowerbot-1.5.0/src/bowerbot/dispatcher.py +84 -0
  9. bowerbot-1.5.0/src/bowerbot/gateway/__init__.py +3 -0
  10. bowerbot-1.5.0/src/bowerbot/project.py +156 -0
  11. bowerbot-1.5.0/src/bowerbot/prompts/__init__.py +26 -0
  12. bowerbot-1.5.0/src/bowerbot/prompts/core.md +9 -0
  13. bowerbot-1.5.0/src/bowerbot/prompts/library.md +64 -0
  14. bowerbot-1.5.0/src/bowerbot/prompts/scene_building.md +347 -0
  15. bowerbot-1.5.0/src/bowerbot/prompts/textures.md +31 -0
  16. bowerbot-1.5.0/src/bowerbot/schemas/__init__.py +51 -0
  17. bowerbot-1.5.0/src/bowerbot/schemas/assets.py +52 -0
  18. bowerbot-1.5.0/src/bowerbot/schemas/intake.py +43 -0
  19. bowerbot-1.5.0/src/bowerbot/schemas/lights.py +42 -0
  20. bowerbot-1.5.0/src/bowerbot/schemas/materials.py +24 -0
  21. bowerbot-1.5.0/src/bowerbot/schemas/textures.py +46 -0
  22. bowerbot-1.5.0/src/bowerbot/schemas/transforms.py +57 -0
  23. bowerbot-1.5.0/src/bowerbot/schemas/validation.py +36 -0
  24. bowerbot-1.5.0/src/bowerbot/services/__init__.py +10 -0
  25. bowerbot-1.5.0/src/bowerbot/services/asset_service.py +372 -0
  26. bowerbot-1.5.0/src/bowerbot/services/library_service.py +27 -0
  27. bowerbot-1.5.0/src/bowerbot/services/light_service.py +360 -0
  28. bowerbot-1.5.0/src/bowerbot/services/material_service.py +215 -0
  29. bowerbot-1.5.0/src/bowerbot/services/stage_service.py +166 -0
  30. bowerbot-1.5.0/src/bowerbot/services/texture_service.py +26 -0
  31. bowerbot-1.5.0/src/bowerbot/services/validation_service.py +45 -0
  32. bowerbot-1.5.0/src/bowerbot/skills/__init__.py +46 -0
  33. bowerbot-1.5.0/src/bowerbot/skills/base.py +152 -0
  34. bowerbot-1.5.0/src/bowerbot/skills/registry.py +168 -0
  35. bowerbot-1.5.0/src/bowerbot/state.py +65 -0
  36. bowerbot-1.5.0/src/bowerbot/token_manager.py +367 -0
  37. bowerbot-1.5.0/src/bowerbot/tools/__init__.py +15 -0
  38. bowerbot-1.5.0/src/bowerbot/tools/_helpers.py +36 -0
  39. bowerbot-1.5.0/src/bowerbot/tools/asset_tools.py +290 -0
  40. bowerbot-1.5.0/src/bowerbot/tools/library_tools.py +96 -0
  41. bowerbot-1.5.0/src/bowerbot/tools/light_tools.py +293 -0
  42. bowerbot-1.5.0/src/bowerbot/tools/material_tools.py +238 -0
  43. bowerbot-1.5.0/src/bowerbot/tools/stage_tools.py +248 -0
  44. bowerbot-1.5.0/src/bowerbot/tools/texture_tools.py +94 -0
  45. bowerbot-1.5.0/src/bowerbot/tools/validation_tools.py +63 -0
  46. bowerbot-1.5.0/src/bowerbot/utils/__init__.py +4 -0
  47. bowerbot-1.5.0/src/bowerbot/utils/asset_folder_utils.py +336 -0
  48. bowerbot-1.5.0/src/bowerbot/utils/asset_intake_utils.py +583 -0
  49. bowerbot-1.5.0/src/bowerbot/utils/dependency_utils.py +127 -0
  50. bowerbot-1.5.0/src/bowerbot/utils/geometry_utils.py +168 -0
  51. bowerbot-1.5.0/src/bowerbot/utils/library_utils.py +136 -0
  52. bowerbot-1.5.0/src/bowerbot/utils/light_utils.py +338 -0
  53. bowerbot-1.5.0/src/bowerbot/utils/material_utils.py +282 -0
  54. bowerbot-1.5.0/src/bowerbot/utils/naming_utils.py +34 -0
  55. bowerbot-1.5.0/src/bowerbot/utils/stage_utils.py +555 -0
  56. bowerbot-1.5.0/src/bowerbot/utils/texture_utils.py +62 -0
  57. bowerbot-1.5.0/src/bowerbot/utils/validation_utils.py +173 -0
@@ -0,0 +1,597 @@
1
+ Metadata-Version: 2.4
2
+ Name: bowerbot
3
+ Version: 1.5.0
4
+ Summary: AI-powered 3D scene assembly from natural language using OpenUSD
5
+ Keywords: usd,openusd,3d,scene-assembly,ai,llm,digital-twin
6
+ Author: Arturo Morales Rangel
7
+ Author-email: Arturo Morales Rangel <arangel@binarycore.us>
8
+ License-Expression: Apache-2.0
9
+ Requires-Dist: click>=8.1
10
+ Requires-Dist: pydantic>=2.0
11
+ Requires-Dist: pydantic-settings>=2.0
12
+ Requires-Dist: rich>=13.0
13
+ Requires-Dist: litellm>=1.40
14
+ Requires-Dist: httpx>=0.27
15
+ Requires-Dist: usd-core>=24.11
16
+ Requires-Dist: fastapi>=0.111 ; extra == 'api'
17
+ Requires-Dist: uvicorn[standard]>=0.30 ; extra == 'api'
18
+ Requires-Dist: websockets>=12.0 ; extra == 'api'
19
+ Requires-Dist: pytest>=8.0 ; extra == 'dev'
20
+ Requires-Dist: pytest-asyncio>=0.23 ; extra == 'dev'
21
+ Requires-Dist: ruff>=0.4 ; extra == 'dev'
22
+ Requires-Dist: mypy>=1.10 ; extra == 'dev'
23
+ Requires-Dist: pre-commit>=3.7 ; extra == 'dev'
24
+ Requires-Python: >=3.12
25
+ Provides-Extra: api
26
+ Provides-Extra: dev
27
+ Description-Content-Type: text/markdown
28
+
29
+ <div align="center">
30
+
31
+ <img src="docs/mascot.png" alt="BowerBot" width="200">
32
+
33
+ # BowerBot
34
+
35
+ **AI-powered OpenUSD scene assembly.**
36
+
37
+ **From empty scene to structured OpenUSD stage in seconds.**
38
+
39
+ [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
40
+ [![Python](https://img.shields.io/badge/Python-3.12+-blue)](https://www.python.org/)
41
+ [![OpenUSD](https://img.shields.io/badge/OpenUSD-25.x-green)](https://openusd.org)
42
+ [![YouTube](https://img.shields.io/badge/YouTube-Tutorials-red?logo=youtube)](https://www.youtube.com/playlist?list=PLhNtBS4KXazZk_LSZfMHlzmNQPqHc4CMb)
43
+ [![Built by Binary Core LLC](https://img.shields.io/badge/Built%20by-Binary%20Core%20LLC-black)](https://binarycore.us)
44
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)
45
+
46
+ </div>
47
+
48
+ ---
49
+
50
+ ## 🐦 Meet BowerBot
51
+
52
+ In the rainforests of Australia and New Guinea lives one of nature's most remarkable architects: the **bowerbird**.
53
+
54
+ Instead of relying on appearance, the bowerbird **collects, curates, and arranges objects** from its environment into a carefully constructed 3D composition. Every object is chosen. Every placement is intentional.
55
+
56
+ **BowerBot brings that same idea to 3D pipelines.**
57
+
58
+ BowerBot is an **AI-assisted OpenUSD scene assembly tool**. It helps you go from an empty scene to a structured, usable layout in seconds by:
59
+ - finding assets from any connected source (Sketchfab, local disk, company DAM, or any custom provider)
60
+ - placing them with spatial awareness
61
+ - organizing them into a valid USD stage
62
+
63
+ **It does not try to replace the artist.**
64
+ It accelerates the part that is repetitive, mechanical, and time-consuming.
65
+
66
+ You stay in control of composition, lighting, and final polish.
67
+ BowerBot gets you there faster.
68
+
69
+ ---
70
+
71
+ ## 🎯 What BowerBot Is (and Is Not)
72
+
73
+ **BowerBot is:**
74
+ - A scene bootstrapper for OpenUSD pipelines
75
+ - A fast way to go from 0 β†’ structured scene
76
+ - A conversational interface for asset search, placement, and layout
77
+ - A pipeline assistant that handles technical correctness (units, hierarchy, references)
78
+ - A pipeline guardian that catches asset issues **before** they reach production
79
+
80
+ **BowerBot is NOT:**
81
+ - A final scene generator
82
+ - A replacement for DCC tools like Maya or Omniverse
83
+ - A system that produces perfect composition or artistic layouts
84
+
85
+ Scenes generated by BowerBot are meant to be **opened, reviewed, and refined** in your DCC.
86
+
87
+ Think of it as:
88
+ > **"Block out the scene instantly, then refine like a pro."**
89
+
90
+ ### Pipeline Quality Built In
91
+
92
+ BowerBot enforces [ASWF USD standards](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md) at every step, not just placing assets. Fixable mismatches (non-canonical folder names, external dependencies) are auto-normalized on intake so the project copy is always self-contained. Unfixable violations (wrong root prim type, missing `defaultPrim`, incorrect `metersPerUnit`, circular references, missing dependencies) are caught **at assembly time** with a clear message about what's wrong and how to fix it.
93
+
94
+ This means problems that would normally surface weeks later in lighting, rendering, or review (broken references, unit mismatches, non-compliant asset structure) are caught **the moment the asset enters the scene**. Fix it once at the source, and every downstream department benefits.
95
+
96
+ > **"The cheapest bug to fix is the one you catch before it enters the pipeline."**
97
+
98
+ ### Who It's For
99
+
100
+ BowerBot targets any team assembling OpenUSD scenes, including:
101
+
102
+ - 🏭 **Digital twin**: warehouses, retail floors, and factory scenes composed from vendor and internal assets
103
+ - πŸ€– **Robotics and simulation**: USD environments for Isaac Sim, Isaac Lab, and custom sim stacks
104
+ - πŸ›οΈ **Architecture and AEC**: bootstrapping interior and exterior USD scenes for visualization and design review
105
+ - πŸ₯½ **XR and spatial computing**: lightweight `.usdz` bundles for Vision Pro, Quest, and ARKit
106
+ - 🎬 **VFX and animation**: shot-dressing and layout over ASWF-structured assets
107
+ - πŸ› οΈ **Pipeline TDs**: a scriptable, LLM-driven scene assembler extendable with custom skills
108
+
109
+ ---
110
+
111
+ ## ✨ What It Does
112
+
113
+ ```
114
+ $ bowerbot open coffee_shop
115
+
116
+ You: Search my Sketchfab for a round bistro table and download it
117
+ BowerBot: Found "Round Bistro Table". Downloaded to assets/Round_Bistro_Table.usdz
118
+
119
+ You: Place it at the center of the room
120
+ BowerBot: Placed at /Scene/Furniture/Round_Bistro_Table_01 (5.0, 0.0, 4.0)
121
+
122
+ You: Find an espresso cup in my local assets and place it on the table surface
123
+ BowerBot: Found espresso_cup.usdz. Table surface at Y=0.74.
124
+ Placed at /Scene/Products/Espresso_Cup_01 (5.0, 0.74, 4.0)
125
+
126
+ You: Move the cup to the left side of the table
127
+ BowerBot: Moved to (4.7, 0.74, 4.0) (within table bounds).
128
+
129
+ You: Validate and package
130
+ BowerBot: All checks passed. Packaged to scenes/coffee_shop/scene.usdz
131
+ ```
132
+
133
+ Open the `.usda` in Maya, usdview, Omniverse, or any USD-compatible DCC to refine composition, lighting, and materials.
134
+
135
+ BowerBot gets you to a clean, structured starting pointβ€”fast.
136
+ From there, you take it to production quality.
137
+
138
+ Projects are persistent. Close the session, come back later, and continue where you left off.
139
+
140
+ ---
141
+
142
+ ## ✨ Features
143
+
144
+ - πŸ“¦ **OpenUSD native**: references, `defaultPrim`, `metersPerUnit`, `upAxis`, all correct out of the box
145
+ - πŸ—οΈ **ASWF-compliant asset folders**: geometry, materials, and lighting split into a root + layer files, per the [USD Working Group guidelines](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md)
146
+ - 🧳 **Self-contained intake**: non-canonical source folders are detected via USD composition, canonicalized (`root.usd` β†’ `<folder>.usda`), and external dependencies (textures, sublayers) are localized into the asset folder so the project copy is always portable
147
+ - 🎨 **Material binding**: apply MaterialX or existing `.usda` materials to specific mesh parts
148
+ - πŸ’‘ **Native USD lighting**: sun, dome, point, area, disk, and tube lights at scene or asset level
149
+ - 🧩 **Automatic unit handling**: assets in cm, mm, or inches are scaled correctly at reference time
150
+ - πŸ“ **Geometry-aware placement**: bounding-box resolved positions for surface, above, below, or nested placements
151
+ - πŸ”Œ **Pluggable skills**: connect any asset source (Sketchfab, PolyHaven, company DAM, or build your own)
152
+ - πŸ—£οΈ **Conversational assembly**: guide scene construction through natural language
153
+ - 🧠 **Multi-LLM support**: OpenAI, Anthropic, and any provider via [litellm](https://docs.litellm.ai/)
154
+ - πŸ“ **Project-based workflow**: one folder per scene, resumable across sessions
155
+ - βœ… **Scene validation**: `defaultPrim`, units, up-axis, reference resolution, and material binding checks
156
+ - πŸ“¦ **USDZ packaging**: export for Apple Vision Pro, Omniverse, or any USD viewer
157
+ - πŸ—οΈ **Onboarding wizard**: zero-config setup in 60 seconds
158
+
159
+ Built on [OpenUSD](https://openusd.org), the [ASWF USD Working Group](https://wiki.aswf.io/display/WGUSD) standards, and the [Alliance for OpenUSD (AOUSD)](https://aousd.org/) core spec driven by Pixar, Apple, NVIDIA, and others.
160
+
161
+ ---
162
+
163
+ ## πŸ”„ How It Works
164
+
165
+ BowerBot is conversational: you tell it what you want and it uses the right tools to build your scene. Behind the scenes, it manages asset discovery, USD composition, materials, lighting, and more.
166
+
167
+ ### Asset Discovery
168
+
169
+ BowerBot searches for assets across all connected sources, prioritizing what's already available:
170
+
171
+ 1. **Local assets first**: BowerBot checks your local asset directory (`assets_dir` in config.json) for USD files (`.usd`, `.usda`, `.usdc`, `.usdz`). This includes anything you've exported from Maya, Houdini, Blender, or any DCC tool, as well as assets previously downloaded from cloud providers.
172
+
173
+ 2. **Cloud providers if needed**: If the asset isn't found locally, BowerBot searches connected providers like Sketchfab, and downloads the asset to your local directory. Future skills will add support for PolyHaven, Fab, CGTrader, Objaverse, and custom company DAMs.
174
+
175
+ 3. **All downloads are cached locally**: Once an asset is downloaded from any source, it lives in your `assets_dir` and is available for all future projects without re-downloading.
176
+
177
+ ### Scene Assembly
178
+
179
+ When you ask BowerBot to place an asset, it routes by what the source looks like and always produces a self-contained ASWF folder in the project:
180
+
181
+ - **Folder with a detectable root** (canonical `wall/wall.usda`, or non-canonical `wall/root.usd` + `wall/geo.usd` + `wall/mtl.usd`): the root is identified via USD composition (the file no sibling depends on), the folder is copied into the project, the root is canonicalized to `<folder>.usda`, sibling references are rewritten, and any externally-referenced textures or layers are localized into the folder so the output is portable.
182
+ - **Loose USD geometry** (`.usd`, `.usda`, `.usdc` from your DCC exports): wrapped in a fresh ASWF folder named after the file stem, producing `<stem>/<stem>.usda` + `geo.usda`.
183
+ - **USDZ files** (from Sketchfab, DAMs, etc.): placed as-is since they're already self-contained.
184
+
185
+ When an asset can't be safely intaken (missing external dependencies, or a folder with multiple independent USDs and no clear root), BowerBot refuses with a message naming the conflict instead of guessing.
186
+
187
+ ### Material Workflow
188
+
189
+ When you apply materials to an asset, BowerBot writes them into the asset folder's `mtl.usda`, not the scene file. The scene stays clean with only references:
190
+
191
+ ```
192
+ You: Apply wood material to the table top
193
+ BowerBot: [searches local assets for "wood" materials]
194
+ [discovers mesh parts: table top, legs, frame]
195
+ [writes material definition + binding into assets/table/mtl.usda]
196
+ Bound /table/mtl/wood_varnished to table top
197
+ ```
198
+
199
+ The result is a production-ready asset folder:
200
+ ```
201
+ assets/single_table/
202
+ single_table.usda <- root (references geo + mtl)
203
+ geo.usda <- geometry (untouched from source)
204
+ mtl.usda <- materials inline + bindings
205
+ ```
206
+
207
+ ### Scene Output
208
+
209
+ The scene file (`scene.usda`) contains only references and lights: no material data, no geometry copies, no sublayers. Clean and readable:
210
+
211
+ ```usda
212
+ def Xform "Scene" (kind = "assembly") {
213
+ def Xform "Furniture" {
214
+ def Xform "Table_01" {
215
+ xformOp:translate = (5, 0, 4)
216
+ xformOp:scale = (0.01, 0.01, 0.01)
217
+ def Xform "asset" (
218
+ references = @assets/single_table/single_table.usda@
219
+ ) { }
220
+ }
221
+ }
222
+ def Xform "Lighting" {
223
+ def DistantLight "Sun_01" { ... }
224
+ def DomeLight "Environment_01" { ... }
225
+ }
226
+ }
227
+ ```
228
+
229
+ Open it in Maya, Omniverse, usdview, or any USD-compatible tool to refine.
230
+
231
+ ---
232
+
233
+ ## πŸš€ Quick Start
234
+
235
+ Requires [uv](https://docs.astral.sh/uv/) (handles Python automatically).
236
+
237
+ ```bash
238
+ # Clone
239
+ git clone https://github.com/binary-core-llc/bowerbot.git
240
+ cd bowerbot
241
+
242
+ # Install
243
+ uv sync
244
+
245
+ # First-time setup (creates ~/.bowerbot/config.json)
246
+ uv run bowerbot onboard
247
+
248
+ # Create a project and start building
249
+ uv run bowerbot new "Coffee Shop"
250
+ uv run bowerbot open coffee_shop
251
+ ```
252
+
253
+ The onboard wizard asks for your LLM API key and the directories for your asset library and projects. Everything is stored in `~/.bowerbot/config.json`. Skills are extension packages you install separately; after `pip install bowerbot-skill-<name>`, add the skill's config to the `skills` block of `config.json`.
254
+
255
+ ---
256
+
257
+ ## πŸ“Ί Tutorials
258
+
259
+ New to BowerBot? Watch the **[tutorial playlist on YouTube](https://www.youtube.com/playlist?list=PLhNtBS4KXazZk_LSZfMHlzmNQPqHc4CMb)** for setup walkthroughs, scene building demos, and tips for working with USD pipelines.
260
+
261
+ ---
262
+
263
+ ## πŸ› οΈ CLI Commands
264
+
265
+ | Command | Description |
266
+ |---------|-------------|
267
+ | `bowerbot new "name"` | Create a new project |
268
+ | `bowerbot open name` | Open a project and start chatting |
269
+ | `bowerbot list` | Show all projects |
270
+ | `bowerbot chat` | Auto-detect project in current directory |
271
+ | `bowerbot build "prompt"` | Single-shot build (auto-creates project) |
272
+ | `bowerbot skills` | List scene builder tools and enabled skills |
273
+ | `bowerbot info` | Show current configuration |
274
+ | `bowerbot onboard` | First-time setup wizard |
275
+
276
+ ---
277
+
278
+ ## πŸ“ Projects
279
+
280
+ Each project is a self-contained folder with metadata, scene, assets, and packaged output in one place:
281
+
282
+ ```
283
+ scenes/coffee_shop/
284
+ project.json # Metadata: name, created_at, updated_at, scene_file
285
+ scene.usda # The USD stage (references only, clean and readable)
286
+ scene.usdz # Packaged output (Apple Vision Pro, Omniverse, etc.)
287
+ assets/ # ASWF folders + self-contained USDZs used by this scene
288
+ textures/ # Scene-level textures (HDRI maps for DomeLights, etc.)
289
+ ```
290
+
291
+ Projects are resumable. Close the session, come back later, and continue where you left off:
292
+
293
+ ```
294
+ $ bowerbot open coffee_shop
295
+ # Project: Coffee Shop
296
+ # Scene: scene.usda (5 object(s))
297
+
298
+ You: Show me the scene structure
299
+ BowerBot: Scene has 5 objects...
300
+
301
+ You: Remove Table_03
302
+ BowerBot: Removed /Scene/Furniture/Table_03
303
+ ```
304
+
305
+ ---
306
+
307
+ ## πŸ”Œ Skills
308
+
309
+ Skills extend BowerBot with **external** asset providers, DCC connectors, and simulation runtimes. Each skill is a separate Python package, discovered at runtime through Python entry points (`bowerbot.skills`). To add a provider, you `pip install bowerbot-skill-<name>` and BowerBot picks it up automatically. The skill SDK lives in `bowerbot.skills`; skills themselves ship and version on their own.
310
+
311
+ ### Scene Builder Tools
312
+
313
+ BowerBot's core tools for building USD scenes:
314
+
315
+ | Tool | Description |
316
+ |------|-------------|
317
+ | `create_stage` | Initialize a new USD scene with standard hierarchy |
318
+ | `place_asset` | Add an asset (auto-creates ASWF folder for loose geometry) |
319
+ | `place_asset_inside` | Nest an asset inside an ASWF container's `contents.usda` |
320
+ | `move_asset` | Reposition an existing object without creating duplicates |
321
+ | `compute_grid_layout` | Calculate evenly spaced positions |
322
+ | `list_scene` | Show current scene with positions and bounding boxes |
323
+ | `rename_prim` | Move/rename objects in the hierarchy |
324
+ | `remove_prim` | Delete objects from the scene |
325
+ | `create_light` | Add native USD lights (sun, dome, point, area, disk, tube) |
326
+ | `update_light` | Modify an existing light's properties |
327
+ | `remove_light` | Delete a light from the scene or asset |
328
+ | `create_material` | Author a procedural MaterialX material and bind it to a prim |
329
+ | `bind_material` | Apply a material to a specific mesh part (writes into asset mtl.usda) |
330
+ | `remove_material` | Clear material binding from a prim |
331
+ | `list_materials` | Show all materials and their bindings |
332
+ | `cleanup_unused_materials` | Prune material definitions no prim binds to (per asset or project-wide) |
333
+ | `list_prim_children` | Discover mesh parts inside a referenced asset |
334
+ | `list_project_assets` | Show asset folders with scene usage status |
335
+ | `delete_project_asset` | Remove an asset folder (checks references first) |
336
+ | `delete_project_texture` | Remove a texture file (checks references first) |
337
+ | `search_assets` | Find USD assets in the user's library by keyword (geo, mtl, package) |
338
+ | `list_assets` | List every USD asset in the user's library, classified by category |
339
+ | `search_textures` | Find HDRIs and material maps in the asset library by keyword |
340
+ | `list_textures` | List every HDRI and material map in the asset library |
341
+ | `validate_scene` | Check for USD errors |
342
+ | `package_scene` | Bundle as `.usdz` |
343
+
344
+ ### Extension Skills
345
+
346
+ Each skill ships as a separate pip package and is discovered at runtime via the `bowerbot.skills` entry-point group. A skill bundles its own Python module, a `SKILL.md` that teaches the LLM when and how to use it, and the four-folder layout BowerBot enforces (`schemas/`, `services/`, `tools/`, `utils/`).
347
+
348
+ **Available:**
349
+ - **[bowerbot-skill-sketchfab](https://github.com/binary-core-llc/bowerbot-skill-sketchfab)** β€” searches and downloads models from your own Sketchfab account in USDZ format (your curated assets, not the public marketplace). Install with `pip install bowerbot-skill-sketchfab`.
350
+
351
+ More providers are planned (PolyHaven, Fab, CGTrader, Objaverse). You can write your own for any asset source, DCC, or simulation runtime. See [CONTRIBUTING.md](CONTRIBUTING.md) for the contract and a worked `pyproject.toml` example.
352
+
353
+ ---
354
+
355
+ ## βš™οΈ Configuration
356
+
357
+ All settings live in `~/.bowerbot/config.json`:
358
+
359
+ ```json
360
+ {
361
+ "llm": {
362
+ "model": "gpt-4.1",
363
+ "api_key": "sk-...",
364
+ "temperature": 0.1,
365
+ "max_tokens": 4096,
366
+ "context_window": null,
367
+ "summarization_threshold": 0.75,
368
+ "num_retries": 3,
369
+ "request_timeout": 120.0,
370
+ "max_tool_rounds": 25
371
+ },
372
+ "scene_defaults": {
373
+ "meters_per_unit": 1.0,
374
+ "up_axis": "Y",
375
+ "default_room_bounds": [10.0, 3.0, 8.0]
376
+ },
377
+ "skills": {
378
+ "sketchfab": {
379
+ "enabled": true,
380
+ "config": { "token": "your-sketchfab-token" }
381
+ }
382
+ },
383
+ "assets_dir": "./assets",
384
+ "projects_dir": "./scenes"
385
+ }
386
+ ```
387
+
388
+ Switch models by changing one line:
389
+
390
+ ```json
391
+ { "model": "gpt-4.1" }
392
+ { "model": "anthropic/claude-sonnet-4-6" }
393
+ { "model": "deepseek/deepseek-chat" }
394
+ ```
395
+
396
+ ### Tested Models
397
+
398
+ | Model | Tool Calling | Instruction Following | Recommended |
399
+ |-------|-------------|----------------------|-------------|
400
+ | `gpt-4.1` | Excellent | Excellent | **Yes** (default) |
401
+ | `gpt-4.1-mini` | Good | Good | Yes (budget) |
402
+ | `gpt-4o` | Poor | Poor | No (skips tool calls, ignores SKILL.md) |
403
+ | `anthropic/claude-sonnet-4-6` | Excellent | Excellent | Yes |
404
+
405
+ BowerBot relies heavily on tool calling and SKILL.md instructions. Models that don't follow tool-calling patterns reliably will produce poor results.
406
+
407
+ ### Token Management
408
+
409
+ BowerBot automatically manages conversation context to stay within model limits. Two settings control this:
410
+
411
+ | Setting | Default | Description |
412
+ |---------|---------|-------------|
413
+ | `context_window` | `null` | Context window size in tokens. `null` = auto-detect from the model. |
414
+ | `summarization_threshold` | `0.75` | Fraction of context budget that triggers history summarization. |
415
+
416
+ Additional tuning options (usually don't need changing):
417
+
418
+ | Setting | Default | Description |
419
+ |---------|---------|-------------|
420
+ | `tool_result_age_threshold` | `2` | User turns before old tool results are compressed. |
421
+ | `min_keep_recent` | `6` | Minimum recent messages always kept verbatim. |
422
+ | `summary_max_tokens` | `512` | Max tokens for the summarization LLM call. |
423
+
424
+ ### Tool-Calling Loop
425
+
426
+ BowerBot runs a loop where the LLM requests tool calls, BowerBot executes them, and the results are fed back. Complex requests (e.g. binding materials to many mesh parts at once) can require many rounds.
427
+
428
+ | Setting | Default | Description |
429
+ |---------|---------|-------------|
430
+ | `max_tool_rounds` | `25` | Maximum LLM ↔ tool exchange rounds per request. Increase if BowerBot stops with "Reached maximum tool-calling rounds" on legitimate workflows. |
431
+
432
+ ### Error Recovery
433
+
434
+ BowerBot automatically handles transient API errors:
435
+
436
+ | Setting | Default | Description |
437
+ |---------|---------|-------------|
438
+ | `num_retries` | `3` | Retries for rate limits and transient errors (429, 500, 503). |
439
+ | `request_timeout` | `120.0` | Seconds before a request times out. |
440
+
441
+ - **Rate limits and transient errors** are retried automatically with exponential backoff.
442
+ - **Validation errors** are fed back to the LLM so it can auto-fix issues and re-validate.
443
+ - **Permanent errors** (bad API key, unknown model) show a clear message without crashing.
444
+
445
+ ---
446
+
447
+ ## πŸ—οΈ Architecture
448
+
449
+ BowerBot is organized FastAPI-style:
450
+
451
+ - **schemas/** describe data (pydantic models + enums)
452
+ - **utils/** are pure-function primitives (no `SceneState`, no orchestration)
453
+ - **services/** are state-aware orchestrators, one function per tool, signature `(state, params)`, calls utils and other services freely, raises on errors
454
+ - **tools/** are the LLM-facing surface, thin adapters that guard preconditions, call ONE service, wrap the result in `ToolResult`
455
+
456
+ Adding a feature is the same three-file change every time: schema, service, tool.
457
+
458
+ ```
459
+ src/bowerbot/
460
+ agent.py # LLM tool-calling loop and prompt assembly
461
+ cli.py # Click CLI
462
+ config.py # Settings from ~/.bowerbot/config.json
463
+ project.py # Project lifecycle (create / load / resume)
464
+ state.py # SceneState: the context threaded through every tool handler
465
+ dispatcher.py # Aggregates tool defs + routes tool calls to handlers
466
+ token_manager.py # Conversation compression and summarization
467
+
468
+ prompts/ # LLM instructions as markdown (editable without code changes)
469
+ core.md
470
+ scene_building.md
471
+
472
+ schemas/ # Pydantic models and enums, grouped by domain
473
+ assets.py # Asset formats, categories, ASWF layer names, metadata
474
+ transforms.py # TransformParams, PositionMode, PlacementCategory, SceneObject
475
+ lights.py # LightType, LightParams
476
+ materials.py # MaterialXShaders, ProceduralMaterialParams
477
+ textures.py # HDRI / image / texture-category enums
478
+ validation.py # Severity, ValidationIssue, ValidationResult
479
+
480
+ services/ # State-aware orchestrators, one per tools module
481
+ stage_service.py # create_stage, list_scene, rename_prim, move_asset, ...
482
+ asset_service.py # place_asset, place_asset_inside, list/delete_project_*
483
+ library_service.py # list_assets, search_assets, find_package_for
484
+ light_service.py # create_light, update_light, remove_light
485
+ material_service.py # create_material, bind_material, list/remove/cleanup
486
+ texture_service.py # list_textures, search_textures
487
+ validation_service.py # validate_scene, package_scene
488
+
489
+ tools/ # LLM-facing API layer (tool defs + thin handlers)
490
+ _helpers.py # Precondition guards (require_stage / project / library)
491
+ stage_tools.py # create_stage, list_scene, rename_prim, move_asset, ...
492
+ asset_tools.py # place_asset, place_asset_inside, list/delete_project_*
493
+ library_tools.py # search_assets, list_assets
494
+ light_tools.py # create_light, update_light, remove_light
495
+ material_tools.py # create_material, bind_material, list/remove_material
496
+ texture_tools.py # search_textures, list_textures
497
+ validation_tools.py # validate_scene, package_scene
498
+
499
+ skills/ # Skill SDK. The contract every skill implements.
500
+ # Skills themselves ship as separate pip packages.
501
+ base.py # Skill, SkillContext, SkillConfigError,
502
+ # SkillCategory, Tool, ToolResult
503
+ registry.py # Entry-point discovery and tool routing
504
+
505
+ utils/ # Pure-function primitives shared by services
506
+ stage_utils.py # Stage create/open/save, references, transforms, prims,
507
+ # ref-path scanning, LIGHT_CLASSES
508
+ asset_intake_utils.py # intake_folder, intake_usdz, create_asset_folder, ASWF
509
+ asset_folder_utils.py # ASWF folder primitives (detect root, layer scopes,
510
+ # resolve_asset_dir_for_prim)
511
+ library_utils.py # scan_library, find_package_for
512
+ light_utils.py # light_in_folder primitives, HDRI staging
513
+ material_utils.py # material_in_folder primitives, find_first_material
514
+ texture_utils.py # find_textures, copy_texture_to_project
515
+ validation_utils.py # validate_stage, package_to_usdz
516
+ geometry_utils.py # Bounds, unit conversion, layout math
517
+ dependency_utils.py # USD dependency tree walker
518
+ naming_utils.py # Name sanitization for files, prims, projects
519
+ gateway/ # Future: FastAPI + MCP server
520
+ ```
521
+
522
+ **Design principles**
523
+
524
+ - **One tools module, one service**: every service module backs exactly one tool surface, with one orchestrator per tool. Shared primitives live in `utils/`, called freely by any service.
525
+ - **Functions only in tools / services / utils**: classes live in `schemas/` (pydantic models, enums) and a small set of state objects (`SceneState`, `Project`).
526
+ - **Tools are thin**: guard preconditions, call ONE service, wrap in `ToolResult`. No business logic, no util calls, no cross-service routing.
527
+ - **Services own orchestration**: take `(state, params)`, do the cross-service and multi-util work, mutate state, raise on errors.
528
+ - **Utils are pure primitives**: no `SceneState`, no other services. Composable building blocks.
529
+ - **State lives in one place**: `SceneState` holds the open stage, the project binding, the asset library path, and the object counter; tool handlers thread it into service calls.
530
+ - **All `pxr` is in `services/` and `utils/`**: the rest of the codebase never imports `pxr` directly.
531
+ - **Prompts are content**: editable `.md` files, not Python constants.
532
+ - **Skills are external integrations**: new asset providers ship as Python packages discovered via entry points.
533
+ - **One config file**: `~/.bowerbot/config.json`, no `.env`.
534
+
535
+ ---
536
+
537
+ ## πŸ“ USD Compliance
538
+
539
+ Every scene follows [OpenUSD](https://openusd.org) best practices and the [ASWF asset structure guidelines](https://github.com/usd-wg/assets/blob/main/docs/asset-structure-guidelines.md):
540
+
541
+ **Scene level**
542
+ - `metersPerUnit = 1.0`, `upAxis = "Y"`, `defaultPrim` always set
543
+ - Standard hierarchy: `/Scene/Architecture`, `/Scene/Furniture`, `/Scene/Products`, `/Scene/Lighting`, `/Scene/Props`
544
+ - References only: no inline geometry, no scattered material sublayers
545
+ - Wrapper-prim pattern isolates scene-level transforms from asset-internal ones, so DCC export transforms (Maya pivots, rotations) stay untouched
546
+ - Pre-packaging validator checks `defaultPrim`, units, up-axis, reference resolution, and material bindings
547
+
548
+ **Asset level**
549
+ - References (not sublayers) per ASWF guidelines, for predictable opinion strength
550
+ - Materials inline in `mtl.usda`, lights inline in `lgt.usda`, nested references in `contents.usda`
551
+ - Automatic `metersPerUnit` conversion across composition boundaries
552
+
553
+ ---
554
+
555
+ ## πŸ—ΊοΈ Roadmap
556
+
557
+ What's next for BowerBot. Contributions welcome:
558
+
559
+ - [ ] **USD Variant Sets**: ASWF-compliant variants on asset root prims (materials, geometry, lighting, configurations, and more)
560
+ - [ ] **Scene templates**: JSON-driven scene assembly with asset resolution
561
+ - [ ] **DCC exporter**: Maya/Houdini tool to export scene layout as BowerBot JSON
562
+ - [ ] **More asset providers**: Fab, PolyHaven, Objaverse, CGTrader skills
563
+ - [ ] **MCP Gateway**: FastAPI server for web UI and external AI clients
564
+ - [ ] **Web UI**: chat panel + live 3D viewport
565
+ - [ ] **BowerHub**: community skill registry
566
+
567
+ ---
568
+
569
+ ## 🀝 Contributing
570
+
571
+ BowerBot is open source and welcomes contributions. The best way to start is writing a new **skill** for an asset provider, DCC, or simulation runtime you use. Skills ship as separate pip packages discovered through the `bowerbot.skills` entry-point group.
572
+
573
+ Read [CONTRIBUTING.md](CONTRIBUTING.md) for the skill contract, the required FastAPI internal layout, and a worked `pyproject.toml` example for a stand-alone skill package.
574
+
575
+ ---
576
+
577
+ ## πŸ“„ License
578
+
579
+ ```
580
+ Copyright 2026 Binary Core LLC
581
+
582
+ Licensed under the Apache License, Version 2.0 (the "License");
583
+ you may not use this file except in compliance with the License.
584
+ You may obtain a copy of the License at
585
+
586
+ http://www.apache.org/licenses/LICENSE-2.0
587
+ ```
588
+
589
+ ---
590
+
591
+ <div align="center">
592
+
593
+ Built with 🐦 by [Binary Core LLC](https://binarycore.us)
594
+
595
+ *"The bowerbird doesn't have the flashiest feathers. It just builds the most compelling world."*
596
+
597
+ </div>