flash

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Runpod Flash

Runpod Flash

Write code locally, iterate with
flash dev
— it runs your functions on remote Runpod GPUs/CPUs with hot-reload and live worker logs — then
flash deploy
to ship.
Endpoint
handles provisioning.
Load on demand — this skill keeps the mental model + gotchas inline; details live in
reference/
:
NeedRead
Install, auth,
flash init
, and the full
flash
command list
reference/setup-and-cli.md
Endpoint(...)
constructor params,
NetworkVolume
/
PodTemplate
/
EndpointJob
, GPU & CPU enum tables
reference/api.md
Worked patterns — choosing a model, warm-worker model loading, CPU→GPU pipeline, parallel callsreference/patterns.md
Quick start:
uv tool install runpod-flash
flash login
(or
export RUNPOD_API_KEY=...
) →
flash init my-project
flash dev
. Details in reference/setup-and-cli.md.
本地编写代码,通过
flash dev
进行迭代——它会在远程Runpod GPU/CPU上运行你的函数,并支持热重载和实时工作日志——随后使用
flash deploy
完成部署。
Endpoint
负责资源调度。
按需查阅——本工具将核心概念与常见陷阱整合在文档内;详细内容请查看
reference/
目录:
需求查阅文档
安装、认证、
flash init
及完整
flash
命令列表
reference/setup-and-cli.md
Endpoint(...)
构造函数参数、
NetworkVolume
/
PodTemplate
/
EndpointJob
、GPU与CPU枚举表
reference/api.md
实践模式——模型选择、预热工作进程模型加载、CPU→GPU流水线、并行调用reference/patterns.md
快速开始:
uv tool install runpod-flash
flash login
(或
export RUNPOD_API_KEY=...
)→
flash init my-project
flash dev
。详细步骤请查看reference/setup-and-cli.md

Dev vs Deploy

开发(Dev)与部署(Deploy)对比

  • flash dev
    iterate. Local server at
    :8888
    , but your decorated functions execute on remote GPU/CPU workers. Hot-reloads on save and streams the worker's logs live to the terminal. No build/upload/deploy wait — use this the whole time you develop.
  • flash deploy
    ship. Builds an artifact and deploys a stable endpoint. Slow (build + upload + provision); only do this once the code works under
    flash dev
    .
flash dev
ships only the function body to the worker, so a
NameError
for a module-level name surfaces immediately here.
flash deploy
imports the whole module and can mask that bug (see Gotcha #1). Develop against
flash dev
and you catch it first.
  • flash dev
    迭代开发。本地服务器运行在
    :8888
    端口,但你标记的函数会在远程GPU/CPU工作进程上执行。保存代码时自动热重载,并将工作进程的日志实时流式传输到终端。无需等待构建/上传/部署——开发全程均可使用该命令。
  • flash deploy
    发布上线。构建工件并部署稳定的端点。速度较慢(需构建+上传+资源调度);仅当代码在
    flash dev
    环境下运行正常后再执行此命令。
flash dev
仅将函数体发送到工作进程,因此模块级名称的
NameError
会立即暴露。而
flash deploy
会导入整个模块,可能掩盖此类bug(参见陷阱#1)。基于
flash dev
进行开发可提前发现这类问题。

Autonomous Dev Loop

自主开发循环

flash dev
is a long-running server. Three rules:
  • Run it in the background — don't block on it.
  • Capture its output to a log file.
  • Drive it over HTTP.
The captured log is the remote worker's live stream (cold start, model load,
print
s, tracebacks) — read it to debug.
bash
flash dev > /tmp/flash-dev.log 2>&1 &                          # background; never run it blocking
for i in $(seq 1 60); do grep -q "flash dev  localhost:" /tmp/flash-dev.log && break; sleep 2; done  # bounded ~2min; if it never appears, check the log for errors
URL=$(grep -o "localhost:[0-9]*" /tmp/flash-dev.log | head -1)               # actual port (8888 bumps if taken)
curl -s "$URL/main/predict" -d '{"data": {...}}'               # dispatches to the remote worker
  • Read the real URL from the log — flash auto-bumps the port if 8888 is in use, and prints
    ✓ flash dev  localhost:<port>
    plus the route table.
  • Routes are namespaced by file:
    main.py
    's
    /predict
    is served at
    /main/predict
    .
  • Two route shapes, two body shapes (mismatch →
    422
    naming the missing field in
    loc
    ):
    • Load-balanced (
      @api.post("/predict")
      ) →
      POST /main/predict
      , body is the arg at top level: a handler
      def predict(data: dict)
      wants
      {"data": {...}}
      (not the bare object).
    • Queue-based (bare
      @Endpoint
      decorator) →
      POST /main/runsync
      (the local dev server only generates
      /runsync
      ; production also exposes
      /run
      ), body is double-wrapped in
      input
      : a handler
      def synthesize(data: dict)
      wants
      {"input": {"data": {...}}}
      . The outer
      input
      is the queue envelope; the inner key is the handler's param name.
  • Edit a handler and save — hot-reload re-syncs the body; just re-send the request, no redeploy. Add
    --auto-provision
    to skip the first-call cold start.
    kill %1
    when done.
flash dev
是一个长期运行的服务器。遵循以下三条规则:
  • 在后台运行——不要阻塞终端。
  • 将输出捕获到日志文件
  • 通过HTTP调用
捕获的日志包含远程工作进程的实时流(冷启动、模型加载、
print
输出、回溯信息)——可通过阅读日志进行调试。
bash
flash dev > /tmp/flash-dev.log 2>&1 &                          # 后台运行;切勿阻塞式运行
for i in $(seq 1 60); do grep -q "flash dev  localhost:" /tmp/flash-dev.log && break; sleep 2; done  # 最多等待约2分钟;若始终未出现该信息,请检查日志中的错误
URL=$(grep -o "localhost:[0-9]*" /tmp/flash-dev.log | head -1)               # 实际端口(若8888被占用会自动切换)
curl -s "$URL/main/predict" -d '{"data": {...}}'               # 调度到远程工作进程
  • 从日志中读取真实URL——若8888端口被占用,flash会自动切换端口,并打印
    ✓ flash dev  localhost:<port>
    及路由表。
  • 路由按文件命名空间划分
    main.py
    中的
    /predict
    路由会在
    /main/predict
    提供服务。
  • 两种路由格式,两种请求体格式(不匹配会返回
    422
    并指出
    loc
    中缺失的字段):
    • 负载均衡式
      @api.post("/predict")
      )→
      POST /main/predict
      ,请求体为顶层参数:处理函数
      def predict(data: dict)
      需要
      {"data": {...}}
      (而非裸对象)。
    • 队列式(仅使用
      @Endpoint
      装饰器)→
      POST /main/runsync
      (本地开发服务器仅生成
      /runsync
      ;生产环境还会暴露
      /run
      ),请求体需双层包裹
      input
      中:处理函数
      def synthesize(data: dict)
      需要
      {"input": {"data": {...}}}
      。外层
      input
      是队列信封;内层键名需与处理函数的参数名一致。
  • 编辑处理函数并保存——热重载会重新同步函数体;只需重新发送请求,无需重新部署。添加
    --auto-provision
    参数可跳过首次调用的冷启动。完成后执行
    kill %1
    停止服务。

Endpoint: Three Modes

Endpoint:三种模式

Full constructor params and the GPU/CPU enum tables are in reference/api.md.
完整的构造函数参数及GPU/CPU枚举表请查看reference/api.md

Mode 1: Your Code (Queue-Based Decorator)

模式1:自定义代码(队列式装饰器)

One function = one endpoint with its own workers.
python
from runpod_flash import Endpoint, GpuGroup

@Endpoint(name="my-worker", gpu=GpuGroup.AMPERE_80, workers=5, dependencies=["torch"])
async def compute(data):
    import torch  # MUST import inside function (cloudpickle)
    return {"sum": torch.tensor(data, device="cuda").sum().item()}

result = await compute([1, 2, 3])
一个函数对应一个独立工作进程的端点。
python
from runpod_flash import Endpoint, GpuGroup

@Endpoint(name="my-worker", gpu=GpuGroup.AMPERE_80, workers=5, dependencies=["torch"])
async def compute(data):
    import torch  # 必须在函数内部导入(cloudpickle要求)
    return {"sum": torch.tensor(data, device="cuda").sum().item()}

result = await compute([1, 2, 3])

Mode 2: Your Code (Load-Balanced Routes)

模式2:自定义代码(负载均衡路由)

Multiple HTTP routes share one pool of workers.
python
from runpod_flash import Endpoint, GpuGroup

api = Endpoint(name="my-api", gpu=GpuGroup.ADA_24, workers=(1, 5), dependencies=["torch"])

@api.post("/predict")
async def predict(data: list[float]):
    import torch
    return {"result": torch.tensor(data, device="cuda").sum().item()}

@api.get("/health")
async def health():
    return {"status": "ok"}
多个HTTP路由共享一个工作进程池。
python
from runpod_flash import Endpoint, GpuGroup

api = Endpoint(name="my-api", gpu=GpuGroup.ADA_24, workers=(1, 5), dependencies=["torch"])

@api.post("/predict")
async def predict(data: list[float]):
    import torch
    return {"result": torch.tensor(data, device="cuda").sum().item()}

@api.get("/health")
async def health():
    return {"status": "ok"}

Mode 3: External Image (Client)

模式3:外部镜像(客户端)

Deploy a pre-built Docker image and call it via HTTP.
python
from runpod_flash import Endpoint, GpuGroup, PodTemplate

server = Endpoint(
    name="my-server",
    image="my-org/my-image:latest",
    gpu=GpuGroup.AMPERE_80,
    workers=1,
    env={"HF_TOKEN": "xxx"},
    template=PodTemplate(containerDiskInGb=100),
)
部署预构建的Docker镜像并通过HTTP调用。
python
from runpod_flash import Endpoint, GpuGroup, PodTemplate

server = Endpoint(
    name="my-server",
    image="my-org/my-image:latest",
    gpu=GpuGroup.AMPERE_80,
    workers=1,
    env={"HF_TOKEN": "xxx"},
    template=PodTemplate(containerDiskInGb=100),
)

LB-style

负载均衡风格

result = await server.post("/v1/completions", {"prompt": "hello"}) models = await server.get("/v1/models")
result = await server.post("/v1/completions", {"prompt": "hello"}) models = await server.get("/v1/models")

QB-style

队列风格

job = await server.run({"prompt": "hello"}) # optional: webhook="https://..." for completion callback await job.wait() print(job.output)

Connect to an existing endpoint by ID (no provisioning):

```python
ep = Endpoint(id="abc123")
job = await ep.runsync({"prompt": "hello"})  # runsync wraps this as {"input": {"prompt": "hello"}}
print(job.output)
job = await server.run({"prompt": "hello"}) # 可选:设置webhook="https://..."接收完成回调 await job.wait() print(job.output)

通过ID连接到已存在的端点(无需调度资源):

```python
ep = Endpoint(id="abc123")
job = await ep.runsync({"prompt": "hello"})  # runsync会自动包装为{"input": {"prompt": "hello"}}
print(job.output)

How Mode Is Determined

模式判定规则

ParametersMode
name=
only
Decorator (your code)
image=
set
Client (deploys image, then HTTP calls)
id=
set
Client (connects to existing, no provisioning)
The table above is how the mode is picked from params. When to reach for
image=
:
参数模式
仅设置
name=
装饰器模式(自定义代码)
设置
image=
客户端模式(部署镜像后通过HTTP调用)
设置
id=
客户端模式(连接到已存在的端点,无需调度资源)
上表说明了如何通过参数选择模式。以下是何时选择
image=
的场景:

When to use
image=
(custom container) vs your own code

何时使用
image=
(自定义容器)而非自定义代码

Default to writing Python (decorator / routes) — it runs arbitrary code with
dependencies=[...]
/
system_dependencies=[...]
and needs no Dockerfile. Even large HuggingFace models stay in decorator mode (weights stream at runtime — see reference/patterns.md → Loading ML models). Reach for
image=
only when you need:
  • a pre-built inference server — vLLM, TensorRT-LLM (
    image="vllm/vllm-openai:latest"
    , or
    runpod/worker-vllm
    ,
    runpod/worker-comfy
    )
  • system-level deps not pip-installable — a specific CUDA/cuDNN, OS libraries
  • models baked into the image — to skip the runtime download entirely
  • an existing Runpod Serverless worker — you already have a working image
Trade-off:
image=
mode can't run arbitrary Python (the image owns all logic) and the image must implement a Runpod Serverless handler. Full list + examples: https://docs.runpod.io/flash/custom-docker-images
默认优先编写Python代码(装饰器/路由模式)——该模式支持通过
dependencies=[...]
/
system_dependencies=[...]
运行任意代码,无需Dockerfile。即使是大型HuggingFace模型也可使用装饰器模式(运行时流式下载权重——参见reference/patterns.md → 加载ML模型)。仅在以下场景下使用
image=
  • 预构建的推理服务器——vLLM、TensorRT-LLM(例如
    image="vllm/vllm-openai:latest"
    ,或
    runpod/worker-vllm
    runpod/worker-comfy
  • 无法通过pip安装的系统级依赖——特定版本的CUDA/cuDNN、操作系统库
  • 内置模型的镜像——跳过运行时下载步骤
  • 已有的Runpod无服务器工作进程——你已经有一个可正常运行的镜像
权衡:
image=
模式无法运行任意Python代码(所有逻辑由镜像控制),且镜像必须实现Runpod无服务器处理函数。完整列表及示例:https://docs.runpod.io/flash/custom-docker-images

Gotchas

常见陷阱

  1. Only the function body ships to the worker -- most common error. Put imports and any module-level constants/helpers the function uses inside the decorated body.
    flash deploy
    imports the whole module so module globals happen to work;
    flash dev
    ships just the body, so a module-level name raises
    NameError
    . A handler that works deployed can break under dev — fix it by moving everything inside.
  2. Forgetting await -- all decorated functions and client methods need
    await
    .
  3. Missing dependencies -- must list in
    dependencies=[]
    .
  4. gpu/cpu are exclusive -- pick one per Endpoint.
  5. idle_timeout is seconds -- default 60s, not minutes.
  6. 10MB payload limit -- pass URLs, not large objects. Return binary (audio/images/files) as base64 in the JSON (
    {"audio_b64": ...}
    ) and decode client-side; for larger outputs write to a NetworkVolume or upload to storage and return a URL.
  7. Client vs decorator --
    image=
    /
    id=
    = client. Otherwise = decorator.
  8. Auto GPU switching requires workers >= 5 -- pass a list of GPU types (e.g.
    gpu=[GpuGroup.ADA_24, GpuGroup.AMPERE_80]
    ) and set
    workers=5
    or higher. The platform only auto-switches GPU types based on supply when max workers is at least 5.
  9. runsync
    timeout is 60s
    -- cold starts can exceed 60s. Use
    ep.runsync(data, timeout=120)
    for first requests or use
    ep.run()
    +
    job.wait()
    instead.
  10. Request body shape (raw/external HTTP callers only) -- match the request shape to the endpoint type:
    • LB routes (
      @api.post(...)
      ): send the handler arg at the top level —
      {"data": {...}}
      .
    • QB endpoints (bare
      @Endpoint
      , hit via
      .../run
      or
      .../runsync
      ): the worker calls
      handler(**job_input)
      , so the request's
      input
      keys must match the handler's parameter names —
      def transcribe(input_data: dict)
      wants
      {"input": {"input_data": {...}}}
      , and
      def read(input: dict)
      wants
      {"input": {"input": {...}}}
      . A mismatch fails with
      got an unexpected keyword argument …
      . Use
      **kwargs
      if the handler ignores the payload.
    • Never send an empty
      input
      .
      A QB request with
      {"input": {}}
      is rejected by the worker SDK as
      Job has missing field(s): id or input
      — always include at least one key.
    • Context: the flash client (
      ep.runsync(x)
      ,
      api.post(...)
      ) hides the spreading, so this only bites raw HTTP/external callers (mismatch behavior verified 2026-07-10 via worker logs). See Autonomous Dev Loop.
  11. Load a model once per worker (not per call) -- for real inference use a class
    @Endpoint
    whose
    __init__
    loads the model once per worker (see reference/patterns.md → Loading ML models). In function-form, reconcile with #1 by caching in a module global inside the body so it works under both
    flash dev
    and
    deploy
    :
    python
    global _MODEL
    try: _MODEL
    except NameError: _MODEL = load_model()   # runs once per worker, reused across calls
  12. Native CUDA libs go in
    dependencies=[]
    too
    -- e.g. CTranslate2/faster-whisper needs
    nvidia-cublas-cu12
    +
    nvidia-cudnn-cu12
    or it silently falls back to CPU. Add them alongside the Python package.
  13. Silent 401 auth failure -- a set
    RUNPOD_API_KEY
    env var overrides the
    flash login
    token, so a bad/expired key wins. The failure is quiet: provisioning logs
    GraphQL request failed: 401
    , but
    flash dev
    still prints its normal ready line ("failed endpoints deploy on-demand"), so it looks healthy. When endpoints fail to provision:
    1. Check the provisioning log for
      GraphQL request failed: 401
      .
    2. Verify the current key independently:
      curl -s -o /dev/null -w '%{http_code}' https://rest.runpod.io/v1/endpoints -H "Authorization: Bearer $RUNPOD_API_KEY"
      (200 = good, 401 = bad).
    3. Fix it:
      unset RUNPOD_API_KEY
      to fall back to the
      flash login
      token, or
      export
      a valid key.
  14. system_dependencies=
    adds to cold start
    -- apt packages (e.g.
    ["ffmpeg", "espeak-ng"]
    ) install on the worker before first use, so the initial call is slower (on top of any model download); warm calls are unaffected.
  15. Teardown a deployed app with
    flash app delete <app>
    --
    flash undeploy list
    may show "no endpoints" for an app that is deployed and serving;
    flash app delete
    (or
    runpodctl serverless delete <id>
    ) reliably removes it.
  1. 仅函数体被发送到工作进程——最常见的错误。将导入语句及函数使用的所有模块级常量/辅助代码放在装饰器内部的函数体中。
    flash deploy
    会导入整个模块,因此模块全局变量可能正常工作;但
    flash dev
    仅发送函数体,因此模块级名称会触发
    NameError
    。在部署环境下正常运行的处理函数可能在开发环境中出错——解决方法是将所有相关代码移到函数体内部。
  2. 忘记使用await——所有装饰器修饰的函数及客户端方法都需要使用
    await
  3. 缺失依赖——必须在
    dependencies=[]
    中列出所有依赖。
  4. GPU/CPU互斥——每个Endpoint只能选择其中一种。
  5. idle_timeout单位为秒——默认60秒,而非分钟。
  6. 请求体大小限制为10MB——传递URL而非大型对象。返回二进制数据(音频/图片/文件)时需在JSON中以base64格式编码(例如
    {"audio_b64": ...}
    ),并在客户端解码;对于更大的输出,可写入NetworkVolume或上传到存储服务后返回URL。
  7. 客户端模式与装饰器模式区分——设置
    image=
    /
    id=
    即为客户端模式,否则为装饰器模式。
  8. 自动切换GPU需要workers≥5——传入GPU类型列表(例如
    gpu=[GpuGroup.ADA_24, GpuGroup.AMPERE_80]
    )并设置
    workers=5
    或更高。仅当最大工作进程数至少为5时,平台才会根据供应情况自动切换GPU类型。
  9. runsync
    超时时间为60秒
    ——冷启动可能超过60秒。首次请求时使用
    ep.runsync(data, timeout=120)
    ,或改用
    ep.run()
    +
    job.wait()
  10. 请求体格式(仅原生/外部HTTP调用者适用)——请求体格式需与端点类型匹配:
    • 负载均衡路由
      @api.post(...)
      ):将处理函数的参数放在顶层——
      {"data": {...}}
    • 队列式端点(仅使用
      @Endpoint
      ,通过
      .../run
      .../runsync
      调用):工作进程会调用**
      handler(**job_input)
      **,因此请求的
      input
      键名必须与处理函数的参数名一致——
      def transcribe(input_data: dict)
      需要
      {"input": {"input_data": {...}}}
      ,而
      def read(input: dict)
      需要
      {"input": {"input": {...}}}
      。不匹配会触发
      got an unexpected keyword argument …
      错误。若处理函数忽略请求体,可使用
      **kwargs
    • 切勿发送空的
      input
      。队列式请求若包含
      {"input": {}}
      会被工作进程SDK拒绝,提示
      Job has missing field(s): id or input
      ——始终至少包含一个键。
    • 说明:flash客户端(
      ep.runsync(x)
      api.post(...)
      )会自动处理参数展开,因此仅原生HTTP/外部调用者会遇到此问题(不匹配行为已在2026-07-10通过工作进程日志验证)。请查看自主开发循环部分。
  11. 每个工作进程仅加载一次模型(而非每次调用)——实际推理时,请使用类形式的
    @Endpoint
    ,其
    __init__
    方法会在每个工作进程启动时加载一次模型(参见reference/patterns.md → 加载ML模型)。若使用函数形式,需结合陷阱#1,在函数体内部将模型缓存到模块全局变量中,以确保在
    flash dev
    deploy
    环境下均能正常工作:
    python
    global _MODEL
    try: _MODEL
    except NameError: _MODEL = load_model()   # 每个工作进程仅运行一次,跨调用复用
  12. 原生CUDA库也需添加到
    dependencies=[]
    ——例如CTranslate2/faster-whisper需要
    nvidia-cublas-cu12
    +
    nvidia-cudnn-cu12
    ,否则会自动回退到CPU运行。请将它们与Python包一起添加到依赖列表中。
  13. 静默的401认证失败——设置的
    RUNPOD_API_KEY
    环境变量会覆盖
    flash login
    生成的令牌,因此无效/过期的密钥会导致认证失败。失败是静默的:资源调度日志会显示
    GraphQL request failed: 401
    ,但
    flash dev
    仍会打印正常的就绪信息("failed endpoints deploy on-demand"),因此看起来服务正常。当端点无法调度资源时:
    1. 检查资源调度日志中是否存在
      GraphQL request failed: 401
    2. 独立验证当前密钥:
      curl -s -o /dev/null -w '%{http_code}' https://rest.runpod.io/v1/endpoints -H "Authorization: Bearer $RUNPOD_API_KEY"
      (返回200表示正常,401表示无效)。
    3. 修复方法:执行
      unset RUNPOD_API_KEY
      以使用
      flash login
      生成的令牌,或
      export
      一个有效的密钥。
  14. system_dependencies=
    会增加冷启动时间
    ——apt包(例如
    ["ffmpeg", "espeak-ng"]
    )会在工作进程首次使用前安装,因此首次调用会更慢(叠加模型下载时间);预热后的调用不受影响。
  15. 使用
    flash app delete <app>
    删除已部署的应用
    ——
    flash undeploy list
    可能显示某个已部署并提供服务的应用"无端点";使用
    flash app delete
    (或
    runpodctl serverless delete <id>
    )可可靠地删除应用。

Resources

资源