puras

Playground schema conventions

How input_schema and output_schema drive auto-generated UI forms (contentMediaType + x-puras extensions).

A "playground" is any UI that renders a form for a skill's inputs from its declared input_schema and shows the resulting output_schema. The dashboard ships one; you can build your own with the same key.

A bare type: string carries no UX hint. To tell a playground "this is an image upload" or "this should be a dropdown," skill authors add two layers of metadata that JSON Schema validators silently ignore:

  1. contentMediaType — standard JSON Schema (Draft 2020-12). Tells the playground what kind of content the string carries.
  2. x-puras — a Puras-specific extension block with richer widget hints (x-* keys are reserved for non-validating extensions in JSON Schema).

Skills don't need any of this to run — the worker only enforces the type constraints. The metadata is purely for the rendering layer.

The x-puras block

yaml
x-puras:
  widget: image           # see widget vocabulary below
  accept: ["image/jpeg", "image/png", "image/webp"]   # widget-specific
  max_size_mb: 5
  upload: drive           # how the playground should hand the file back
  placeholder: "drag photo here"
  help: "tam boy, net bir fotoğraf önerilir"

All fields are optional. Unknown fields are ignored by the playground (so you can prototype your own).

Widget vocabulary

widgetWhat the playground rendersSchema shape it pairs with
imageDrop-zone + URL field + previewstring (URL) or the polymorphic file object
videoSame as image, video previewstring (URL) or file object
audioFile picker, audio previewstring (URL) or file object
fileGeneric file pickerstring (URL) or file object
drive_filePicker over the project's drivestring (drive_path)
attachmentsMulti-file drop-zonearray of file objects
textSingle-line <input>string
long_text<textarea>string, usually maxLength >= 500
codeMonospace editor (lang from x-puras.lang)string
selectDropdownstring with enum
multiselectMulti-selectarray of string with items.enum
switchToggleboolean
sliderRange slidernumber with minimum + maximum

Default widget if unset:

  • booleanswitch
  • string with enumselect
  • string with contentMediaType matching image/*image
  • string with contentMediaType matching video/*video
  • string with contentMediaType matching audio/*audio
  • string with maxLength >= 500long_text
  • array of strings with items.enummultiselect
  • array of file objects → attachments
  • everything else stringtext

File inputs — two shapes

A skill that needs a binary input can declare it two ways:

A. As a URL string (simplest)

yaml
user_image_url:
  type: string
  format: uri
  contentMediaType: image/*
  x-puras:
    widget: image
    upload: drive
    accept: ["image/jpeg", "image/png", "image/webp"]
    max_size_mb: 5
  description: User's photo URL (HTTPS).

The playground uploads to POST /v1/drive/upload, takes the response's signed_url, and passes that string into inputs.user_image_url. The skill just sees a URL.

B. As a polymorphic file object (puras-native)

yaml
user_image:
  type: object
  x-puras:
    widget: image
    accept: ["image/jpeg", "image/png", "image/webp"]
  oneOf:
    - { required: [drive_path], properties: { drive_path: {type: string} } }
    - { required: [url],        properties: { url: {type: string, format: uri} } }
    - { required: [base64],     properties: { base64: {type: string}, media_type: {type: string} } }

The skill reads it with puras.load_bytes(inputs["user_image"]) or puras.load_path(...) — these helpers accept all three shapes transparently. This form is more flexible (frontend can send a drive_path, an external URL, or inline base64) at the cost of more verbose validation.

Pick A when the skill itself only needs a URL (e.g. handing off to another model). Pick B when the skill code wants raw bytes or a local path.

upload modes

For widgets that produce a file (image, video, audio, file):

  • drive (default) — playground POSTs to /v1/drive/upload and sends the signed URL back into the input. Persists across jobs; recommended for anything > 200 KB.
  • inline — playground base64-encodes and sends inline. Use for tiny payloads only (job inputs live forever in Postgres jsonb).
  • url-only — no upload widget; user pastes a URL. For skills that should only consume public URLs.

Long text and code

yaml
prompt:
  type: string
  minLength: 1
  maxLength: 4000
  x-puras:
    widget: long_text
    placeholder: "Tell the agent what to do"
yaml
sql:
  type: string
  x-puras:
    widget: code
    lang: sql

Enums

enum alone gives you a select. Add labels with x-puras.options:

yaml
tone:
  type: string
  enum: [playful, bold, trustworthy]
  x-puras:
    widget: select
    options:
      - { value: playful, label: "🎉 Playful" }
      - { value: bold, label: "🔥 Bold" }
      - { value: trustworthy, label: "🛡 Trustworthy" }

Arrays

For an array of attachments (e.g. agentic skill that accepts vision inputs):

yaml
attachments:
  type: array
  minItems: 1
  maxItems: 4
  x-puras:
    widget: attachments
    accept: ["image/jpeg", "image/png", "image/webp", "application/pdf"]
    max_size_mb: 5
  items:
    type: object
    properties:
      drive_path: { type: string }
      url:        { type: string, format: uri }
      base64:     { type: string }
      media_type: { type: string }
    additionalProperties: true

Playground renders one drop-zone that accepts multiple files and submits them as the array.

Output rendering

output_schema doesn't get widget hints — playgrounds render it as read-only structured data. But the same contentMediaType convention applies: an output field with contentMediaType: image/* should be rendered as an <img>, audio as <audio>, etc. A playground can also honor x-puras.widget on outputs if you want a specific renderer (e.g. widget: code to syntax-highlight a returned string).

What playgrounds MUST NOT do

  • Hide a field because it has no x-puras block — fall back to defaults.
  • Reject unknown widget values — render a placeholder warning, keep the form usable.
  • Strip x-puras from the request body. Send inputs exactly as the schema describes; the worker only cares about validated values.

See concepts for how skills, inputs, and outputs are stored. See the shipped example-project for a working skill that uses these hints.