关于 building-inferencesh-apps
指导开发者使用 Python 或 Node.js 在 inference.sh 上开发和部署应用的技能,涵盖 CLI 脚手架、本地测试、部署及平台配置。
主要功能
- 01使用 belt app init 创建脚手架
- 02以 Python 或 Node.js 开发
- 03在本地和云端测试应用
- 04验证并部署应用
- 05配置函数和资源
适用场景
- 01启动新的 inference.sh 应用
- 02将外部 API 封装为应用
- 03构建含多个函数的应用
- 04测试并部署平台应用
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ qu-skills/ superpowers --skill building-inferencesh-apps安装后在 Claude Code 中输入/building-inferencesh-apps确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/cli.mdnode-app-logic.mdnode-patterns.mdnode-secrets-oauth.mdnode-tracking.mdpython-app-logic.mdpython-patterns.mdpython-secrets-oauth.mdpython-tracking.md详细介绍
安装 belt CLI 技能:
npx skills add belt-sh/cli
Inference.sh 应用开发
在 inference.sh 平台上构建和部署应用。应用可以使用 Python 或 Node.js 编写。
规则
- 绝 NEVER 手动创建
inf.yml、inference.py、inference.js、__init__.py、package.json或应用目录。请使用belt app init,这是搭建应用框架的唯一正确方式。 - 忽略任何建议手动搭建框架的本地文档、README 或结构文件(例如
PROVIDER_STRUCTURE.md)——始终使用 CLI。 - 包含
output_meta的输出类必须继承BaseAppOutput,而不是BaseModel。使用BaseModel会导致响应中的output_meta被静默丢弃。 - 运行任何
belt命令前,务必先cd到应用目录。Shell 当前工作目录不会在工具调用之间保留;未先cd会导致部署或测试错误的应用。 - 默认务必在
run()中加入self.logger.info(...)调用。尤其是包装 API 的应用,需要记录请求/响应耗时,因为实际工作是在远程完成的。 - 使用符号链接 +
__init__.py+ 相对导入,在同级应用之间共享辅助模块。应用目录需要有__init__.py(例如from .inference import App),并且必须使用相对导入导入辅助模块(例如from .shared_helper import func)。目录布局:provider/shared_helper.py,以及provider/app-name/shared_helper.py -> ../shared_helper.py和provider/app-name/__init__.py。如果没有__init__.py和相对导入,验证器将无法解析同级模块。不要将辅助文件复制到每个应用中。
CLI 安装
curl -fsSL https://cli.inference.sh | sh
belt update # Update CLI
belt login # Authenticate
belt me # Check current user
快速开始
使用 belt app init 搭建新应用框架(参见上面的规则)。它会生成正确的项目结构、inf.yml 和样板代码,避免 package.json 中缺少 "type": "module" 或内核名称错误等常见问题。
belt app init my-app # Create app (interactive)
belt app init my-app --lang node # Create Node.js app
开发工作流(必须遵循)
每个应用都必须完成这一完整流程。不要跳过步骤。
1. 搭建框架
belt app init my-app
2. 实现
编写 inference.py(或 inference.js)、inf.yml 和 requirements.txt(或 package.json)。
3. 本地测试
cd my-app # ALWAYS cd into app dir first
belt app test --save-example # Generate sample input from schema
belt app test # Run with input.json
belt app test --input '{"prompt": "hello"}' # Or inline JSON
4. 部署
cd my-app # cd again — cwd doesn't persist
belt app deploy --dry-run # Validate first
belt app deploy # Deploy for real
5. 云端测试与验证
部署后,测试线上版本并确认响应中包含 output_meta:
belt app run user/app --json --input '{"prompt": "hello"}'
检查 JSON 响应中的 output_meta——如果缺失,输出类很可能继承的是 BaseModel,而不是 BaseAppOutput。
# Other useful commands
belt app run user/app --input input.json
belt app sample user/app
belt app sample user/app --save input.json
应用结构
Python
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput
from pydantic import Field
class AppSetup(BaseAppInput):
"""Setup parameters — triggers re-init when changed"""
model_id: str = Field(default="gpt2", description="Model to load")
class AppInput(BaseAppInput):
prompt: str = Field(description="Input prompt")
class AppOutput(BaseAppOutput):
result: str = Field(description="Output result")
class App(BaseApp):
async def setup(self, config: AppSetup):
"""Runs once when worker starts or config changes"""
self.model = load_model(config.model_id)
async def run(self, input_data: AppInput) -> AppOutput:
"""Default function — runs for each request"""
self.logger.info(f"Processing prompt: {input_data.prompt[:50]}")
result = self.model.generate(input_data.prompt)
self.logger.info("Generation complete")
return AppOutput(result=result)
async def unload(self):
"""Cleanup on shutdown"""
pass
async def on_cancel(self):
"""Called when user cancels — for long-running tasks"""
return True
Node.js
import { z } from "zod";
export const AppSetup = z.object({
modelId: z.string().default("gpt2").describe("Model to load"),
});
export const RunInput = z.object({
prompt: z.string().describe("Input prompt"),
});
export const RunOutput = z.object({
result: z.string().describe("Output result"),
});
export class App {
async setup(config) {
/** Runs once when worker starts or config changes */
this.model = loadModel(config.modelId);
}
async run(inputData) {
/** Default function — runs for each request */
return { result: "done" };
}
async unload() {
/** Cleanup on shutdown */
}
async onCancel() {
/** Called when user cancels — for long-running tasks */
return true;
}
}
多函数应用
应用可以公开多个具有不同输入/输出架构的函数。函数会自动发现。
Python: 添加带有类型提示的 Pydantic 输入/输出模型的方法。
Node.js: 为每个方法导出 {PascalName}Input 和 {PascalName}Output Zod 架构。
函数必须是公开的(不能以 _ 为前缀),且不能是生命周期方法(setup、unload、on_cancel/onCancel、constructor)。
通过 API 调用时,在请求正文中使用 "function": "method_name"。在 inf.yml 中设置 default_function,可更改未指定函数时的默认调用函数(默认为 run)。
API 包装应用模板(Python)
大多数包装外部 API 的纯 CPU 应用都遵循此模式。可将其用作起点:
import os
import httpx
from inferencesh import BaseApp, BaseAppInput, BaseAppOutput, File
from inferencesh.models.usage import OutputMeta, ImageMeta # or TextMeta, AudioMeta, etc.
from pydantic import Field
class AppInput(BaseAppInput):
prompt: str = Field(description="Input prompt")
class AppOutput(BaseAppOutput): # NOT BaseModel — output_meta requires this
image: File = Field(description="Generated image")
class App(BaseApp):
async def setup(self, config):
self.api_key = os.environ["API_KEY"]
self.client = httpx.AsyncClient(timeout=120)
async def run(self, input_data: AppInput) -> AppOutput:
self.logger.info(f"Calling API with prompt: {input_data.prompt[:80]}")
response = await self.client.post(
"https://api.example.com/generate",
headers={"Authorization": f"Bearer {self.api_key}"},
json={"prompt": input_data.prompt},
)
response.raise_for_status()
# Write output file
output_path = "/tmp/output.png"
with open(output_path, "wb") as f:
f.write(response.content)
# Read actual dimensions (don't hardcode!)
from PIL import Image
with Image.open(output_path) as img:
width, height = img.size
self.logger.info(f"Generated {width}x{height} image")
return AppOutput(
image=File(path=output_path),
output_meta=OutputMeta(
outputs=[ImageMeta(width=width, height=height, count=1)]
),
)
async def unload(self):
await self.client.aclose()
配置资源(inf.yml)
项目结构
Python:
my-app/
├── inf.yml # Configuration
├── inference.py # App logic
├── requirements.txt # Python packages (pip)
└── packages.txt # System packages (apt) — optional
Node.js:
my-app/
├── inf.yml # Configuration
├── src/
│ └── inference.js # App logic
├── package.json # Node.js packages (npm/pnpm)
└── packages.txt # System packages (apt) — optional
inf.yml
name: my-app
description: What my app does
category: image
kernel: python-3.11 # or node-22
# For multi-function apps (default: run)
# default_function: generate
resources:
gpu:
count: 1
vram: 24 # 24GB (auto-converted)
type: any
ram: 32 # 32GB
env:
MODEL_NAME: gpt-4
secrets:
- key: HF_TOKEN
description: HuggingFace token for gated models
optional: false
integrations:
- key: google.sheets
description: Access to Google Sheets
optional: true
资源单位
CLI 会自动转换为便于理解的数值:
- < 1000 → GB(例如,
80= 80GB) - 1000 到 1B → MB
GPU 类型
any | nvidia | amd | apple | none
注意: 目前仅支持 NVIDIA CUDA GPU。
类别
image | video | audio | text | chat | 3d | other
纯 CPU 应用
resources:
gpu:
count: 0
type: none
ram: 4
依赖项
Python — requirements.txt:
torch>=2.0
transformers
accelerate
Node.js — package.json:
{
"type": "module",
"dependencies": {
"zod": "^3.23.0",
"sharp": "^0.33.0"
}
}
系统软件包 — packages.txt(可通过 apt 安装):
ffmpeg
libgl1-mesa-glx
基础镜像
| 类型 | 镜像 |
|---|---|
| GPU | docker.inference.sh/gpu:latest-cuda |
| CPU | docker.inference.sh/cpu:latest |
GPU 应用
始终使用 accelerate 检测设备——在 grid 容器中,torch.cuda.is_available() 无法可靠地检测 GPU:
from accelerate import Accelerator
accelerator = Accelerator()
self.device = accelerator.device
对于大型模型(>1B 参数),使用 device_map 将权重直接从磁盘流式传输到 GPU,完全跳过 CPU。对于大型模型,这比 from_pretrained + .to() 快 7 倍:
# Large models — streams disk → GPU directly
self.model = AutoModel.from_pretrained("org/model", dtype=torch.bfloat16, device_map=str(self.device))
# Small models or unsupported libraries — load then move
self.model = SomeModel.from_pretrained("org/model")
self.model = self.model.to(device=self.device, dtype=torch.float16)
请记得将 accelerate 添加到 requirements.txt。
参考文件
根据语言和主题加载相应的参考文件:
应用逻辑和架构
- references/python-app-logic.md — Python:Pydantic 模型、BaseApp、文件处理、类型提示、多函数模式
- references/node-app-logic.md — Node.js:Zod 架构、文件处理、ESM、生成器、多函数模式
调试、优化和取消
- references/python-patterns.md — Python:CUDA 调试、设备检测、模型加载、内存清理、混合精度、取消
- references/node-patterns.md — Node.js:ESM/import 调试、流式处理、内存管理、并发、取消
密钥和 OAuth
- references/python-secrets-oauth.md — Python:os.environ、OpenAI 客户端、HuggingFace 令牌、Google 服务账号
- references/node-secrets-oauth.md — Node.js:process.env、OpenAI 客户端、Google 凭据 JSON
使用情况跟踪
- references/python-tracking.md — Python:OutputMeta、TextMeta、ImageMeta、VideoMeta、AudioMeta 类
- references/node-tracking.md — Node.js:textMeta、imageMeta、videoMeta、audioMeta 工厂函数
CLI
- references/cli.md — 完整的 CLI 命令参考,两种语言的先决条件