Creating an App

Scaffold a new app with the CLI.


Create App

bash
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:

bash
1belt app init

The 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:

bash
1belt app init my-vendor --template api

It 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:

bash
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>/:

FileOwnerContents
openapi.plan.yamlyouHow 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.pybeltPydantic 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.pyyouOne thin function per operation calling through inferencesh.upstream. Written once. Quirks, pagination, reshaped responses and output_meta go here.
<app>/inf.ymlyoucredentials: 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/*.jsonbeltOne request per function from the spec's examples, placeholders for required fields without one.
<app>/requirements.txt, pricing.mdyouPinned inferencesh, one pricing row per function. Written once.

Rerun with the plan file to regenerate after a spec change or a plan edit:

bash
1belt app init --openapi acme/openapi.plan.yaml

A 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.md

Files Explained

FilePythonNode.jsPurpose
inf.ymlYesYesApp settings, resources
inference.pyYes—App logic (Python)
src/inference.js—YesApp logic (Node.js)
requirements.txtYes—Python packages (pip)
package.json—YesNode.js packages (npm/pnpm)
packages.txtYesYesSystem packages (apt) — optional
skills/YesYesGuidance for AI coding assistants

Dependencies

App Packages

1torch>=2.02transformers3accelerate

System Packages (packages.txt)

For system-level dependencies (installed via apt):

code
1ffmpeg2libgl1-mesa-glx

Base Images

Apps run in containers with these base images:

TypeImageUse Case
GPUdocker.inference.sh/gpu:latest-cudaCUDA GPU apps
CPUdocker.inference.sh/cpu:latestCPU-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

we use cookies

we use cookies to ensure you get the best experience on our website. for more information on how we use cookies, please see our cookie policy.

by clicking "accept", you agree to our use of cookies.
learn more.