Image, audio and video runs

Job tools that answer with a run: images, video, speech and transcripts.

A job tool (kind generate or transcribe) answers with a run. The app calls the same endpoint, POST /ai/run/<tool>, and gets 202 with the run straight away. Gemmein follows the run until it ends.

What it is. One job on a provider that runs jobs (see Tool kinds and providers) or on your own pipeline, started by a signed-in person on a job tool. The server holds it from start to end: its status, its progress, its price and its outputs.

Does.

  • Reserves the tool's ceiling when the run starts: credits × ceil(maxUnits / per), or the price itself on a per-run tool. A refusal names it ("Poster needs up to 8 credits. You have 4.").
  • Composes the provider request from params and the inputs, on your key, and submits it.
  • Checks every open run every 10 seconds, polling the provider at 5 seconds, then 15, 30 and 60.
  • When the run succeeds, copies every output URL the provider answered into a sealed file on the person: image, audio or video, up to 200 MB each, with the type proven from the bytes. A transcript comes back as text.
  • Settles the hold at what the provider metered (the count of images, the provider's own seconds rounded up, or the request's characters) at the tool's rate, and never above the hold.
  • Ends an open run cancelled on g.runs.cancel, and expired 60 minutes after it started, and tells the provider to stop where it can be told.
  • Records every ended run on ai_calls, and names the run on the ledger's reserve and settle entries.

Does not.

  • Apply a webhook body. A provider's webhook only makes Gemmein check the run sooner, and Gemmein fetches the run again by the provider's own id.
  • Charge a failed, cancelled or expired run. The hold returns whole.
  • Hand a provider a file ref. A ref becomes a short-lived signed link.
  • Let a run bill past maxUnits.

Needs something else when.

  • You want the answer in the same request → a chat tool.
  • Your own server calls a provider → call the provider directly, with your key.

Example. "Poster", replicate, 2 credits per image, up to 4 images a run, with a prompt input.

The tool file, at gemmein/ai/tools/poster.json. The count placeholder stands alone in its string, so the provider receives a number:

{
  "label": "Poster",
  "kind": "generate",
  "provider": "replicate",
  "model": "black-forest-labs/flux-schnell",
  "credits": 2,
  "unit": "images",
  "params": { "prompt": "{{prompt}}", "num_outputs": "{{count}}", "aspect_ratio": "16:9" },
  "inputs": [
    { "name": "prompt", "type": "text", "required": true, "maxLength": 1000 },
    { "name": "count", "type": "number" }
  ],
  "bounds": { "maxUnits": 4 }
}

Starting it holds 8 credits. Two images back settle at 4 and return 4. In the app:

const run = await g.runs.start("poster", { prompt: "a lighthouse at dusk", count: 2 });
const done = await g.runs.watch(run.id, { onUpdate: (r) => setProgress(r.progress) });
if (done.status === "succeeded") img.src = done.result.files[0].url;   // a signed link, created for this answer
save(done.result.files[0].ref);   // the ref is what the app stores

A result file's url lapses at urlExpiresAt. Read the run again for a fresh one. The ref stays, and is what g.files reads. The run carries reserved, and once ended charged, units and unitCount.

A key on start (your own name for the run, up to 80 characters) makes a retried start of the same tool answer the same run. g.runs.list({ since }) is the person's own runs, newest first; g.runs.get(id) is one of them.

Runs are the person's, called from the browser. A secret key is refused, and another person's run is unknown_run. Ten runs may be open per person at a time.

StatusMeaning
queuedCreated, the ceiling reserved, the provider not yet asked
runningThe provider has it; progress is 0–100 when the provider says
succeededEnded well; result is set and the hold settled at what was metered
failedThe provider gave up; error says why; the hold returned
cancelledg.runs.cancel ended it; the hold returned
expired60 minutes passed with no end; the hold returned; the provider told to stop

Run refusals are in the codes table. start also meets every refusal g.ai.run does for the tool itself: unknown_tool, tool_disabled, entitlement_required, payload_too_large, ai_capped, session_required, scope_denied.

Was this page helpful?

Last updated