Scaffold a new app with the CLI.
Create App
1# Python app (default)2belt app init my-app34# Node.js app5belt app init my-app --lang node67# Set the title shown on the app's page (default: made from the name)8belt app init my-app --title "My App"Or interactive mode:
1belt app initThe interactive wizard asks for name, title, description, category, kernel, and language. For category, the CLI loads the public store list from GET /store/categories?type=app (sorted by rank, same slugs as the Grid). Pick a slug from the menu; it is written to inf.yml as category.
If the category list cannot be loaded (offline, API error, or empty response), the CLI prompts for a category slug as plain text (default placeholder other). Deploy does not check the slug against that list; fix category in inf.yml later or run belt app deploy --meta after the app exists.
Non-interactive belt app init my-app still uses the default category from the scaffold template until you edit inf.yml.
Vendor API apps
A wrapper around a vendor's HTTP API is an ordinary Python app with one function per upstream operation. The api template scaffolds one by hand:
1belt app init my-vendor --template apiIt gives you an httpx client with retries, a credential declaration for the key, test_requests/ per function, a pricing.md skeleton and a README with the finishing steps. (belt 1.22+)
From an OpenAPI spec
When the vendor publishes an OpenAPI 2.0, 3.0 or 3.1 document, belt generates the apps from it:
1belt app init --openapi https://docs.tavily.com/documentation/api-reference/openapi.json2belt app init --openapi ./spec.yaml --vendor acme3belt app init --openapi https://api.acme.com/openapi.json --openapi-header "Authorization: Bearer $ACME_KEY"Only a parseable document is accepted. A docs page is not a spec: find the JSON or YAML file it is built from (the vendor's docs often link it, as do their SDK repositories and APIs.guru).
What it writes, under <vendor>/:
| File | Owner | Contents |
|---|---|---|
openapi.plan.yaml | you | How the spec becomes apps: operations grouped into apps (by tag, else by first path segment, else one app named core) with their function names, the security schemes bound to secret names, the credential slug and provider seed. Edit and rerun. |
<app>/_generated.py | belt | Pydantic models for every request and response, one Operation descriptor per function (method, path, where each field goes, auth, MCP annotations from the HTTP method, per-function credentials from the operation's security) and an Input/Output class per function. Rewritten on every rerun. |
<app>/inference.py | you | One thin function per operation calling through inferencesh.upstream. Written once. Quirks, pagination, reshaped responses and output_meta go here. |
<app>/inf.yml | you | credentials: keyed by the vendor slug with every secret the schemes need and a company: seed, default_function set to the cheapest read, the spec's description. Written once; only the credentials: block follows the plan on reruns. |
<app>/test_requests/*.json | belt | One request per function from the spec's examples, placeholders for required fields without one. |
<app>/requirements.txt, pricing.md | you | Pinned inferencesh, one pricing row per function. Written once. |
Rerun with the plan file to regenerate after a spec change or a plan edit:
1belt app init --openapi acme/openapi.plan.yamlA regeneration rewrites _generated.py, test_requests/ and the credentials: block of inf.yml. An operation the spec no longer has is refused by name, so a renamed operationId surfaces instead of silently dropping a function; operations the plan does not list are reported so you can add them to an app. A function you drop from the plan keeps its method in inference.py and its row in pricing.md until you remove them; the rerun names both.
The vendor credential must hold every secret of every scheme the plan lists, or the app stays "setup required" for everyone. Drop a scheme nobody can provide, and the functions that need it, rather than inventing a second key for the vendor.
Response models are lenient (every field optional), so a vendor answer that strays from its spec does not fail the call; the unchanged response is always on the output's raw. Request models are strict: required input is required.
What the CLI leaves to you, in order: check the secret names in the plan against belt credentials list (an existing credential for the vendor already names its keys), regroup or rename functions in the plan, test each function (belt app test --function <name> --input test_requests/<name>.json), finish inference.py, fill pricing.md and metadata.tags, deploy.
Models come from datamodel-code-generator, run through uv (uvx). Without uv, install it yourself and point BELT_DATAMODEL_CODEGEN at the datamodel-codegen command. (belt 1.25+, inferencesh 0.29+)
What's Created
1my-app/2├── inf.yml # Configuration3├── inference.py # Your code4├── requirements.txt # Python dependencies5├── packages.txt # System dependencies (optional)6├── skills/ # AI coding agent guidance7└── README.mdFiles Explained
| File | Python | Node.js | Purpose |
|---|---|---|---|
inf.yml | Yes | Yes | App settings, resources |
inference.py | Yes | — | App logic (Python) |
src/inference.js | — | Yes | App logic (Node.js) |
requirements.txt | Yes | — | Python packages (pip) |
package.json | — | Yes | Node.js packages (npm/pnpm) |
packages.txt | Yes | Yes | System packages (apt) — optional |
skills/ | Yes | Yes | Guidance for AI coding assistants |
Dependencies
App Packages
1torch>=2.02transformers3accelerateSystem Packages (packages.txt)
For system-level dependencies (installed via apt):
1ffmpeg2libgl1-mesa-glxBase Images
Apps run in containers with these base images:
| Type | Image | Use Case |
|---|---|---|
| GPU | docker.inference.sh/gpu:latest-cuda | CUDA GPU apps |
| CPU | docker.inference.sh/cpu:latest | CPU-only apps |
Note: Currently only NVIDIA CUDA GPUs are supported for GPU apps.
Python environment
Each Python app version gets its own venv. It starts with build tools (setuptools, setuptools-scm, wheel, wheel-stub, build, packaging, ninja, scikit-build-core, cython, flit-core, numpy) plus pydantic and inferencesh.
torch and its CUDA wheels, accelerate, huggingface_hub, transformers and diffusers are not preinstalled. List every package your code imports in requirements.txt. transformers and diffusers need accelerate at runtime for device_map, low_cpu_mem_usage and enable_model_cpu_offload().
App versions that already have an environment keep it. New versions, and versions set up on a new worker, get this base.
Next
→ App Code