> For the complete documentation index, see [llms.txt](/llms.txt). Every page on this site is also available as markdown at `<path>.md`.

# Studio

Studio apps are local registries of one or more workspace runtime bindings.
The CLI imports a `FlowAIApp` and serves the Studio UI and API from the same
Python process.

## CLI

The Python app entrypoint uses the `package.module:symbol` import form. The
symbol can be a `FlowAIApp` value or a zero-argument factory returning one.

```bash
flowai-harness dev --app my_agent.studio_app:app
flowai-harness serve --app my_agent.studio_app:app
```

Both commands bind to `127.0.0.1:4111` by default and serve the packaged
Studio UI on the same port as the API.

Common options:

| Option | Description |
| --- | --- |
| `--app package.module:symbol` | Required Studio app import target. |
| `--host 127.0.0.1` | Host to bind. |
| `--port 4111` | Port for both Studio UI and API routes. |
| `--no-studio` | Serve API routes only. |

For Python harness apps, use `dev` or `serve`.

`dev` additionally accepts four flags for running the Studio frontend from
source instead of the packaged UI assets:

| Option | Description |
| --- | --- |
| `--no-frontend` | Do not launch the React Studio source frontend dev server. |
| `--frontend-host HOST` | Host for the React Studio source frontend dev server. Requires `--studio-dir`. Defaults to `127.0.0.1`. |
| `--frontend-port PORT` | Port for the React Studio source frontend dev server. Requires `--studio-dir`. Defaults to `3000`. |
| `--studio-dir PATH` | Path to the Studio frontend source directory containing `package.json`. When provided, `dev` starts Bun/Vite on a separate frontend port. |

These source-frontend flags cannot be combined with `--no-studio`, and
`--no-frontend` cannot be combined with the other three.

## HTTP surface

Studio serves a dynamic config file and workspace-scoped API routes:

| Route | Description |
| --- | --- |
| `/` | Packaged Studio browser UI. |
| `/__flowai_config.js` | Dynamic runtime config for the browser UI. |
| `/api/status` | Studio API version and implementation status. |
| `/api/workspaces` | Workspace list and default workspace key. |
| `/api/workspaces/{workspace_key}/runtime` | Runtime metadata. |
| `/api/workspaces/{workspace_key}/agents` | Agent metadata. |
| `/api/workspaces/{workspace_key}/data/...` | Data inspection routes when a data environment is attached. |
| `/api/workspaces/{workspace_key}/tests/...` | Test case management routes. |
| `/api/workspaces/{workspace_key}/evals/...` | Eval routes. |
| `/api/workspaces/{workspace_key}/runs/...` | Persisted run activity, events, approvals, and traces. |

`/__flowai_config.js` is generated on each request. The server does not mutate
installed Studio files.

## API reference

<span id="flowai_harness.studio.define_app"></span>

## `define_app`

`define_app(*, name: 'str', workspaces: 'Mapping[str, WorkspaceRuntimeBinding] | None' = None, default_workspace: 'str' = 'default', description: 'str | None' = None, metadata: 'Mapping[str, Any] | None' = None, runtime_spec: 'RuntimeSpec | Mapping[str, Any] | None' = None, runtime_factory: 'RuntimeFactory | None' = None, runtime: 'Any | None' = None, resource_id: 'str | None' = None, capabilities: 'list[str] | tuple[str, ...] | None' = None, data_environment: 'Mapping[str, Any] | None' = None, target_database_url: 'str | None' = None) -> 'FlowAIApp'`

<table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td><code>str</code></td>
<td>required</td>
</tr>
<tr>
<td><code>workspaces</code></td>
<td><code>Mapping[str, WorkspaceRuntimeBinding] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>default_workspace</code></td>
<td><code>str</code></td>
<td><code>'default'</code></td>
</tr>
<tr>
<td><code>description</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>metadata</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>runtime_spec</code></td>
<td><code>RuntimeSpec | Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>runtime_factory</code></td>
<td><code>RuntimeFactory | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>runtime</code></td>
<td><code>Any | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>resource_id</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>capabilities</code></td>
<td><code>list[str] | tuple[str, ...] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>data_environment</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>target_database_url</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
</tbody>
</table>

<p><strong>Returns:</strong> <code>FlowAIApp</code></p>

Define a local Studio app registry.

Passing `runtime_spec` is single-runtime sugar. It creates a `default`
workspace binding.

Args:
    name: App name; also used as the app id.
    workspaces: Mapping from workspace key to
        ``WorkspaceRuntimeBinding``. Mutually exclusive with
        ``runtime_spec``.
    default_workspace: Key of the workspace served by default; must be
        registered.
    description: Optional app description.
    metadata: Free-form app metadata.
    runtime_spec: Single-runtime sugar; creates one ``default``
        workspace binding from this spec.
    runtime_factory: Zero-argument runtime factory for the sugar
        binding.
    runtime: Pre-built native runtime for the sugar binding.
    resource_id: Tenant resource id override for the sugar binding.
    capabilities: Capability ids for the sugar binding.
    data_environment: Data environment for the sugar binding.
    target_database_url: Target database URL shorthand for the sugar
        binding.

Returns:
    A ``FlowAIApp`` servable with ``flowai-harness dev`` / ``serve``.

Raises:
    ValueError: If both ``workspaces`` and ``runtime_spec`` are given,
        neither is given, sugar-only options are combined with
        ``workspaces``, ``default_workspace`` is not registered, or a
        workspace key is empty or duplicated.
    TypeError: If a workspaces value is not a
        ``WorkspaceRuntimeBinding``.

<span id="flowai_harness.studio.define_workspace_runtime"></span>

## `define_workspace_runtime`

`define_workspace_runtime(*, runtime_spec: 'RuntimeSpec | Mapping[str, Any]', runtime_factory: 'RuntimeFactory | None' = None, runtime: 'Any | None' = None, workspace_key: 'str | None' = None, display_name: 'str | None' = None, description: 'str | None' = None, resource_id: 'str | None' = None, metadata: 'Mapping[str, Any] | None' = None, capabilities: 'list[str] | tuple[str, ...] | None' = None, data_environment: 'Mapping[str, Any] | None' = None, target_database_url: 'str | None' = None) -> 'WorkspaceRuntimeBinding'`

<table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>runtime_spec</code></td>
<td><code>RuntimeSpec | Mapping[str, Any]</code></td>
<td>required</td>
</tr>
<tr>
<td><code>runtime_factory</code></td>
<td><code>RuntimeFactory | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>runtime</code></td>
<td><code>Any | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>workspace_key</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>display_name</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>description</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>resource_id</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>metadata</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>capabilities</code></td>
<td><code>list[str] | tuple[str, ...] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>data_environment</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>target_database_url</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
</tbody>
</table>

<p><strong>Returns:</strong> <code>WorkspaceRuntimeBinding</code></p>

Define a workspace runtime binding for local Studio.

Args:
    runtime_spec: ``RuntimeSpec`` or mapping validated as one.
    runtime_factory: Zero-argument factory constructing the native
        runtime lazily on first use. Mutually exclusive with
        ``runtime``. Defaults to ``create_runtime(spec,
        data_environment=...)``.
    runtime: Pre-built native runtime handle to reuse.
    workspace_key: Stable workspace key; usually assigned by
        ``define_app(...)`` from the workspaces mapping key.
    display_name: Human-readable workspace name; derived from the
        workspace key when omitted.
    description: Optional workspace description shown in Studio.
    resource_id: Tenant resource id override; defaults to the spec
        tenant's ``resource_id``.
    metadata: Free-form workspace metadata.
    capabilities: Capability ids advertised to Studio. Defaults to the
        local base capabilities, plus the data capabilities when a data
        environment is attached.
    data_environment: Rust data dependencies for built-in toolkits;
        validated against the workspace tenant.
    target_database_url: Shorthand for
        ``data_environment["target_database_url"]``.

Returns:
    A ``WorkspaceRuntimeBinding`` for registration with
    ``define_app(...)``.

Raises:
    ValueError: If both ``runtime_factory`` and ``runtime`` are
        supplied, or the data environment conflicts with the workspace
        tenant.

<span id="flowai_harness.studio.FlowAIApp"></span>

## `FlowAIApp`

`FlowAIApp(*, name: 'str', workspaces: 'Mapping[str, WorkspaceRuntimeBinding]', default_workspace: 'str' = 'default', description: 'str | None' = None, metadata: 'Mapping[str, Any] | None' = None) -> 'None'`

<table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>name</code></td>
<td><code>str</code></td>
<td>required</td>
</tr>
<tr>
<td><code>workspaces</code></td>
<td><code>Mapping[str, WorkspaceRuntimeBinding]</code></td>
<td>required</td>
</tr>
<tr>
<td><code>default_workspace</code></td>
<td><code>str</code></td>
<td><code>'default'</code></td>
</tr>
<tr>
<td><code>description</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>metadata</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
</tbody>
</table>

<p><strong>Returns:</strong> <code>None</code></p>

Studio-visible registry of workspace runtime bindings.

<span id="flowai_harness.studio.WorkspaceRuntimeBinding"></span>

## `WorkspaceRuntimeBinding`

`WorkspaceRuntimeBinding(*, runtime_spec: 'RuntimeSpec | Mapping[str, Any]', runtime_factory: 'RuntimeFactory | None' = None, runtime: 'Any | None' = None, workspace_key: 'str | None' = None, display_name: 'str | None' = None, description: 'str | None' = None, resource_id: 'str | None' = None, metadata: 'Mapping[str, Any] | None' = None, capabilities: 'list[str] | tuple[str, ...] | None' = None, data_environment: 'Mapping[str, Any] | None' = None, target_database_url: 'str | None' = None) -> 'None'`

<table>
<thead>
<tr>
<th>Parameter</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>runtime_spec</code></td>
<td><code>RuntimeSpec | Mapping[str, Any]</code></td>
<td>required</td>
</tr>
<tr>
<td><code>runtime_factory</code></td>
<td><code>RuntimeFactory | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>runtime</code></td>
<td><code>Any | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>workspace_key</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>display_name</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>description</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>resource_id</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>metadata</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>capabilities</code></td>
<td><code>list[str] | tuple[str, ...] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>data_environment</code></td>
<td><code>Mapping[str, Any] | None</code></td>
<td><code>None</code></td>
</tr>
<tr>
<td><code>target_database_url</code></td>
<td><code>str | None</code></td>
<td><code>None</code></td>
</tr>
</tbody>
</table>

<p><strong>Returns:</strong> <code>None</code></p>

One Studio workspace bound to one FlowAI runtime spec and factory.
