lace-app-sdk
What gets installed — project layout
A new LACE app is a small, conventional Python package plus two manifests. Everything else is derived from those.
The file tree
acme.field_intake/
├── app/
│ ├── __init__.py
│ ├── data.py # AppDataCollectionSchema — typed, tenant-scoped collections
│ ├── tools.py # @tool handlers + AppToolDescriptor registration
│ ├── agents.py # AgentDefinition + AppAgentProvider
│ ├── routes.py # AppRouteProvider (FastAPI routes)
│ ├── migrations.py # AppDataMigrations / schema evolution
│ ├── jobs.py # @app_task handlers (optional)
│ └── policy.py # AppPolicyProvider (optional)
├── ui/ # federated runtime-UI (optional, Module Federation)
│ ├── src/
│ ├── package.json
│ └── vite.config.ts
├── tests/
│ ├── test_routes.py
│ ├── test_tools.py
│ └── conftest.py
├── lace_app_manifest.json # canonical manifest — what the publisher seals
├── LaceApp.yaml # sidecar / deploy descriptor (image, limits, env, health)
├── compose.yaml # local dev stack (from SDK templates/lace_compose.yaml)
├── pyproject.toml # depends on lace-app-sdk (+ gunicorn/uvicorn/fastapi)
├── .lace/ # local state (git-ignored): dev state, build cache
└── README.md
Two manifests, one app
| File | Owns | When it changes |
|---|---|---|
lace_app_manifest.json | The canonical LaceAppManifest — app_id, data_collections, tools[], route_providers, agent_providers, tool_modules, pipeline_providers, runtime_ui, etc. The publisher seals this. | Written by write_app_manifest() or derived from app/data.py at package time. |
LaceApp.yaml | Sidecar runtime wiring — base_image, health_check_path, env_vars, resource_limits, network_policy, storage. | At scaffold + on deploy (image digest pinned). |
Canonical example — what lace_app_manifest.json looks like after you call write_app_manifest():
jsonlace_app_manifest.json
{
"app_id": "acme.field_intake",
"display_name": "Field Intake",
"version": "0.1.0",
"data_collections": [ ... ],
"route_providers": ["app.routes:RouteProvider"],
"agent_providers": ["app.agents:AgentProvider"],
"tool_modules": ["app.tools"],
"migration_provider": "app.migrations:Migrations"
}The app/ package
app/data.py— one or moreAppDataCollectionSchemaobjects. Each declares a JSON Schema, required fields, identity fields, unique constraints, searchable/sortable fields, and physical storage capabilities (EVENT_LOG,FULL_TEXT_INDEX). See data collections.app/tools.py— tools declared with@tool(tool_id=..., description_for_model=..., input_schema=...). The descriptor is pure data (no dotted refs); the handler lives in the sidecar and is looked up viaAppToolRegistrykeyed by(app_id, tool_id). See tools.app/agents.py— anAgentDefinition(model policy, instructions, capabilities, orchestration) plus anAppAgentProviderthat returns it. The sidecar runs a real agentic loop. See agents.app/routes.py— anAppRouteProviderthat mounts FastAPI routes at/apps/<app_id>/api/*. See routes & UI.app/migrations.py— optionalAppDataMigrationsfor schema evolution (applied through the platform lifecycle).
SDK dependencies
terminalbash
# base install — pure-pydantic contracts + CLI, no lace runtime needed
pip install lace-app-sdk
lace-app --help # works
# inside the monorepo / sidecar — full runtime bindings
pip install lace-app-sdk[runtime]
| What | Depends on | Notes |
|---|---|---|
lace-app-sdk (base) | pydantic>=2 only | Every pure contract (_contracts/*) + all --help/auth/create/clone verbs work with no lace installed. |
lace-app-sdk[runtime] | lace monorepo + sidecar deps | Required for lace-app dev (migration-apply) and lace-app test proof harness. Installed automatically in the sidecar and when running inside the monorepo. |
Your app's pyproject.toml | fastapi, uvicorn, lace-app-sdk | Sidecar image bakes these. Pin what you need; the platform pins the sidecar base. |
Templates & scaffolding
The SDK ships a compose template at lace_app_sdk/templates/lace_compose.yaml and starter scaffolds in lace.builder.starter_scaffolds.
lace-app create --template notes copies the notes_app example (see src/lace_app_sdk/examples/notes_app/) — a complete minimal app with data, routes, and an agent.
What is git-ignored
.lace/, __pycache__/, .venv/, dist/, dist_sdk/. Commit lace_app_manifest.json — the publisher reads it.
Next: the SDK itself — facade, not fork or manifest reference.