Skip to content

Repository files navigation

arcade

One playground for every AI model. Text, image, audio, video — any provider, one UI.

Arcade is a local, definition-driven playground for testing AI APIs. Instead of bouncing between provider dashboards or writing throwaway curl commands, you pick a model, fill in a form tailored to that model's actual inputs, and see the result — rendered inline as an image, audio player, or video, not a raw JSON blob.

Play mode — pick an endpoint, fill the form, generate

Why

Generic API tools show you every possible field regardless of the model you're testing. But an image generation model has completely different inputs than a chat model, and those inputs vary between providers. You end up ignoring half the form and guessing which fields matter.

Arcade solves this with JSON definition files. Each file describes one model endpoint — its parameters, auth, request format, and response structure. Arcade reads the definition and generates the right form with the right controls. Add a new provider by adding a file. No frontend changes. Definitions are portable and can be contributed by anyone.

What makes it different

  • Multimodal in one place — text, image, audio, and video generation across 16 providers
  • Adaptive UI — each model gets a form built from its definition, showing only relevant inputs
  • Compare mode — run two models side by side against the same prompt
  • Full transparency — curl commands (with optional real API key), raw request/response JSON, latency metrics on every call
  • BYOK — bring your own API keys. Keys live in .env, sent only to the provider's API through a local proxy, never persisted or transmitted elsewhere
  • Extensible by design — add a provider by dropping in a JSON file, no code changes needed

Features

Command palette

Hit Cmd+K to open the command palette. Search across all endpoints, bookmarks, and saved configurations. Select an endpoint to see available models, then pick one to load the form.

Command palette — search endpoints and bookmarks

Model picker — choose from available models

Play mode

Pick any endpoint, click an example chip to pre-fill the prompt, and hit Generate. Streaming endpoints show tokens as they arrive with TTFT and tokens/sec metrics.

Example filled — one-click example prompts

Curl preview

Inspect the exact HTTP request before or after sending. Toggle "Include API key" to copy a ready-to-run curl command.

Curl preview — inspect the exact HTTP request

JSON inspector

Switch to the JSON tab to see the full request and response payloads with redacted auth headers.

JSON inspector — full request and response payloads

Compare mode

Run two endpoints side by side against the same prompt. Select with Shift+Enter from the command palette. Shared parameters are merged into one form; endpoint-specific params appear in separate columns.

Compare mode — two endpoints side by side

Compare results — side-by-side output

Compare curl — both requests side by side

Everything else

  • Streaming, polling, and sync — three interaction patterns, chosen per-definition
  • Stream toggle — switch streaming endpoints to sync mode for debugging
  • Output renderers — text (with streaming tokens), images, audio, and video
  • System prompt — inject a system message on any chat-completions endpoint
  • Latency metrics — time-to-first-token and tokens/sec for streaming; total duration for sync
  • Log drawer — expandable panel showing the raw HTTP request/response
  • Bookmarks — save and restore endpoint + param combinations from the command palette
  • Advanced params — collapsible section for sliders (temperature, max tokens, etc.)
  • API key validation — automatic check on startup, status shown per provider

Providers

ProviderDefinitionsTypes
Baseten1chat
Berget1chat
Cerebras1chat
DeepInfra2chat, image
DeepSeek1chat
DigitalOcean4chat, image, TTS, music
Fireworks1chat
Google2chat, image
Groq1chat
Hugging Face1chat
Mistral1chat
OpenAI2chat, image
OpenRouter3chat, image, audio
Perplexity1chat
SambaNova1chat
Together4chat, image, TTS, video

16 providers, 27 definitions.

Quick start

git clone https://github.com/ajot/arcade.git
cd arcade
python -m venv venv &&source venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # add your API keys
python app.py

Open http://localhost:8080.

You only need keys for the providers you want to test. Keys are stored locally in .env, sent only to the provider's API through the local proxy, and never persisted or transmitted elsewhere.

Adding a provider

If you use Claude Code, the repo includes a built-in skill that generates definition files for you. Just describe what you want:

Add a chat completions definition for Mistral

Claude Code will look at existing definitions for the right patterns, write the JSON, validate it, and update .env.example — no manual work needed.

To add a provider manually, create a JSON file in definitions/<provider>/. The definition has four sections:

SectionPurpose
authHow to attach the API key (header name, prefix, env var)
requestURL, method, body template, and parameter definitions
interactionPattern (streaming, polling, or sync) and related config
responseOutput extraction paths and types (text, image, audio, video)

Minimal example — a streaming chat endpoint:

{
"schema_version": 1,
"id": "myprovider-chat",
"provider": "myprovider",
"provider_display_name": "MyProvider",
"name": "MyProvider Chat",
"description": "Chat completions via MyProvider",
"auth": {
"type": "header",
"header": "Authorization",
"prefix": "Bearer ",
"env_key": "MYPROVIDER_API_KEY"
},
"request": {
"method": "POST",
"url": "https://api.myprovider.com/v1/chat/completions",
"content_type": "application/json",
"body_template": { "stream": true },
"params": [
{
"name": "model",
"type": "enum",
"options": ["model-a", "model-b"],
"default": "model-a",
"ui": "dropdown",
"required": true
},
{
"name": "prompt",
"type": "string",
"ui": "textarea",
"required": true,
"placeholder": "Ask anything...",
"body_path": "_chat_message"
}
]
},
"interaction": {
"pattern": "streaming",
"stream_format": "sse",
"stream_path": "$.choices[0].delta.content"
},
"response": {
"outputs": [
{ "path": "$.choices[0].message.content", "type": "text", "source": "inline" }
],
"error": { "path": "$.error.message" }
},
"examples": [
{
"label": "Hello world",
"params": { "prompt": "Say hello in 5 languages.", "model": "model-a" }
}
]
}

Then:

  1. Add MYPROVIDER_API_KEY= to .env.example
  2. Run python validate.py to verify the definition
  3. Restart the server — no code changes needed

Each parameter in request.params needs a ui type that tells Arcade how to render the form control:

UI typeParam typeRenders asKey fields
textareastringMulti-line text inputplaceholder, required, body_path
textstringSingle-line text inputplaceholder, required
dropdownenumSelect menuoptions (required), default
sliderinteger or floatRange slider with numeric displaymin, max, default

Parameter fields reference:

FieldRequiredDescription
nameyesParameter key (used in body and examples)
typeyesstring, integer, float, or enum
uiyestextarea, text, dropdown, or slider
requirednoIf true, must be filled before Generate
body_pathnoDot-separated path in the request body (e.g. input.prompt). Use _chat_message to auto-wrap as an OpenAI-style messages array
optionsenum onlyArray of allowed values for dropdown
defaultnoPre-filled value
min / maxslider onlyRange bounds for the slider
placeholdernoHint text shown in empty inputs
groupnoSet to "advanced" to place in a collapsible section

Contributing

The easiest way to contribute is to add a new provider definition. No frontend or backend code changes needed — just a JSON file.

  1. Fork the repo and create a branch
  2. Add your definition to definitions/<provider>/
    • Use Claude Code with the built-in skill: Add a chat completions definition for <provider>
    • Or write it manually using an existing definition as reference
  3. Run python validate.py to make sure it passes
  4. Add the API key variable to .env.example
  5. Open a PR

Bug fixes, UI improvements, and new interaction patterns are also welcome.

Project structure

arcade/
├── app.py # Flask app — routes, definition loading, API proxy
├── proxy.py # Builds HTTP requests from definitions, extracts responses
├── validate.py # Definition schema validator
├── requirements.txt # flask, requests, python-dotenv, gunicorn
├── .env.example # API key template (16 providers)
├── definitions/ # One JSON file per endpoint (27 definitions)
│ ├── openai/
│ ├── together/
│ ├── digitalocean/
│ └── ...
├── templates/
│ └── index.html # Single-page Jinja2 template with Tailwind CSS
└── static/
├── app.js # Client — streaming, polling, rendering, compare mode
└── style.css # Arcade theme

How it works

  1. On startup, Flask walks definitions/ and loads every JSON file into memory. API keys are read from .env based on each definition's auth.env_key field — no key mapping in code.
  2. The page renders a command palette of all definitions. Picking one fetches its JSON and dynamically builds the form (textareas, dropdowns, sliders) from request.params.
  3. On Generate, the client posts the definition ID and params to the Flask proxy. The proxy merges params into body_template, attaches auth headers, and forwards the request to the provider.
  4. Based on interaction.pattern, the response flows back as streamed SSE tokens, polled status checks, or a single JSON payload. The client reads response.outputs to pick the right renderer — text, image, audio, or video.

About

A local playground for testing AI APIs across every provider.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages