> ## Documentation Index
> Fetch the complete documentation index at: https://na-36-changelog-go-livepeer-2026-05-18.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Is My AI Workload a Good Fit for Livepeer?

> Decision framework for evaluating whether your AI workload belongs on Livepeer. Includes decision tree, capability matrix, gateway vs orchestrator responsibilities, and anti-patterns.

export const FlexContainer = ({children, direction = "row", gap = "var(--lp-spacing-4)", align = "flex-start", justify = "flex-start", wrap = false, marginTop = "", marginBottom = "", style = {}, className = "", ...rest}) => {
  return <div className={className} style={{
    display: "flex",
    flexDirection: direction,
    gap: gap,
    alignItems: align,
    justifyContent: justify,
    flexWrap: wrap ? "wrap" : "nowrap",
    ...marginTop ? {
      marginTop
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", accentBar = "", style = {}, className = "", ...rest}) => {
  const variants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    accent: {
      border: "1px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    muted: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "transparent"
    }
  };
  const accentBarColors = {
    accent: "var(--lp-color-accent)",
    positive: "var(--green-9)"
  };
  return <div data-docs-bordered-box="" data-accent-bar={accentBarColors[accentBar] ? "" : undefined} className={className} style={{
    ...variants[variant],
    padding: padding,
    borderRadius: borderRadius,
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const GotoLink = ({label, relativePath, text = '', icon = 'arrow-turn-down-right', className = '', style = {}, ...rest}) => {
  return <span className={className} style={{
    display: 'inline-flex',
    alignItems: 'center',
    ...style
  }} {...rest}>
      <span style={{
    marginRight: 8
  }}>{text}</span>
      <Icon icon={icon} aria-hidden="true" />
      <a href={relativePath} style={{
    marginLeft: 6
  }}>
        {label}
      </a>
    </span>;
};

export const GotoCard = ({label, relativePath, icon, text, cta = '', className = '', style = {}, ...props}) => {
  icon = icon ? icon : 'arrow-turn-down-right';
  return <Card title={label} icon={icon} href={relativePath} cta={cta} className={className} style={style} {...props}>
      {text}
    </Card>;
};

export const DynamicTable = ({tableTitle = null, headerList = [], itemsList = [], monospaceColumns = [], columnWidths = {}, contentFitColumns = [], showSeparators = false, margin, className = "", style = {}, ...rest}) => {
  if (!headerList.length) {
    return <div>No headers provided</div>;
  }
  const safeContentFitColumns = Array.isArray(contentFitColumns) ? contentFitColumns : [];
  const usesContentFitColumns = safeContentFitColumns.length > 0;
  const isContentFitColumn = header => safeContentFitColumns.includes(header);
  const getColumnStyle = header => {
    const widthStyle = columnWidths[header] ? {
      width: columnWidths[header],
      minWidth: columnWidths[header],
      maxWidth: columnWidths[header]
    } : {};
    const contentFitStyle = !columnWidths[header] && isContentFitColumn(header) ? {
      width: "1%",
      whiteSpace: "nowrap"
    } : {};
    return {
      ...contentFitStyle,
      ...widthStyle
    };
  };
  return <div className={className} style={style} {...rest}>
      {tableTitle && <div style={{
    fontStyle: "italic",
    margin: 0
  }}>
          <strong>{tableTitle}</strong>
        </div>}
      <div style={{
    overflowX: "auto",
    ...margin != null && ({
      margin
    })
  }} role="region" tabIndex={0} aria-label={tableTitle ? `Scrollable table: ${tableTitle}` : "Scrollable table"}>
        <table data-docs-dynamic-table style={{
    width: "100%",
    tableLayout: usesContentFitColumns ? "auto" : "fixed",
    borderCollapse: "collapse",
    fontSize: "0.9rem",
    marginTop: 0
  }}>
          <thead>
            <tr style={{
    backgroundColor: "var(--lp-color-accent)",
    color: "var(--lp-color-on-accent)",
    borderBottom: "1px solid var(--lp-color-border-default)"
  }}>
              {headerList.map((header, index) => <th key={index} style={{
    padding: "10px 8px",
    textAlign: "left",
    fontWeight: "600",
    color: "var(--lp-color-on-accent)",
    ...getColumnStyle(header)
  }}>
                  {header}
                </th>)}
            </tr>
          </thead>
          <tbody>
            {itemsList.filter(item => showSeparators || !item?.__separator).map((item, rowIndex) => item?.__separator ? <tr key={rowIndex} style={{
    backgroundColor: "var(--lp-color-accent)",
    color: "var(--lp-color-on-accent)",
    borderBottom: "1px solid var(--lp-color-accent)"
  }}>
                  <td colSpan={headerList.length} style={{
    padding: "6px 8px",
    fontWeight: "700",
    color: "var(--lp-color-on-accent)",
    letterSpacing: "0.01em"
  }}>
                    {(item[headerList[0]] ?? item.Category) ?? "Category"}
                  </td>
                </tr> : <tr key={rowIndex} style={{
    borderBottom: "1px solid var(--lp-color-border-default)"
  }}>
                  {headerList.map((header, colIndex) => {
    const value = (item[header] ?? item[header.toLowerCase()]) ?? "-";
    const isMonospace = monospaceColumns.includes(colIndex);
    return <td key={colIndex} style={{
      padding: "8px 8px",
      fontFamily: isMonospace ? "monospace" : "inherit",
      wordWrap: "break-word",
      overflowWrap: "break-word",
      ...getColumnStyle(header)
    }}>
                        {isMonospace ? <code>{value}</code> : value}
                      </td>;
  })}
                </tr>)}
          </tbody>
        </table>
      </div>
    </div>;
};

## Decision tree

Livepeer is optimised for **streaming, GPU-bound, low-latency inference**. It is not a general-purpose batch compute or file-processing network. Use this page to determine whether your workload is a good fit before you start building.

```text icon="terminal" theme={null}
Start
 │
 ├── Is the workload STREAMING (frames / chunks / segments)?
 │    └── No  →  ✗ Not a good Livepeer fit
 │
 └── Yes
      │
      ├── Does the workload require GPU-accelerated INFERENCE?
      │    └── No  →  ✗ Use a gateway or standard compute
      │
      └── Yes
           │
           ├── Is LOW LATENCY (< ~500ms) important to the UX?
           │    └── No  →  ⚠ Possible, but not differentiated
           │
           └── Yes
                │
                ├── Does it produce INCREMENTAL output?
                │    └── No  →  ⚠ Marginal fit
                │
                └── Yes  →  ✓ Excellent Livepeer workload
```

<Note>
  **Summary:** Livepeer works best for streaming, GPU-bound inference with low latency and incremental output. If your workload fails the first two gates, don't build it on Livepeer.
</Note>

## Capability matrix

<DynamicTable
  headerList={["Category", "Example workloads", "Fit", "Why"]}
  itemsList={[
{ "Category": "Audio", "Example workloads": "Live ASR, live translation, audio intent", "Fit": "✓ Strong", "Why": "Chunked streams, GPU inference, low latency" },
{ "Category": "Audio", "Example workloads": "Offline transcription", "Fit": "⚠ Medium", "Why": "Works, but batch infra is cheaper" },
{ "Category": "Audio", "Example workloads": "Video → MP3 extraction", "Fit": "✗ Poor", "Why": "CPU task, no inference" },
{ "Category": "Vision", "Example workloads": "Depth, pose, segmentation", "Fit": "✓ Strong", "Why": "Frame-based GPU inference" },
{ "Category": "Vision", "Example workloads": "Image classification (batch)", "Fit": "⚠ Medium", "Why": "Not latency-critical" },
{ "Category": "Video", "Example workloads": "Video-to-video, diffusion, effects", "Fit": "✓ Excellent", "Why": "Core Livepeer strength" },
{ "Category": "Video", "Example workloads": "Transcoding only", "Fit": "⚠ Medium", "Why": "Supported, but not AI-driven" },
{ "Category": "Text", "Example workloads": "Prompt routing, captions, metadata", "Fit": "⚠ Conditional", "Why": "Only if real-time" },
{ "Category": "Text", "Example workloads": "LLM batch inference", "Fit": "✗ Poor", "Why": "Latency-tolerant, expensive on GPU" }
]}
/>

## Gateway vs orchestrator responsibilities by workload

Understanding the split between gateway and orchestrator helps you know where to direct integration effort for each workload type.

### Audio workloads (ASR, translation, intent)

**Gateway** handles: audio ingestion via WebRTC, chunking and buffering, authentication and retries, output aggregation and fan-out.

**Orchestrator** handles: GPU-resident ASR / translation models, streaming inference execution, incremental token emission, language or model specialisation.

### Vision workloads (depth, pose, segmentation)

**Gateway** handles: frame routing, capability selection, latency monitoring, cost-aware routing.

**Orchestrator** handles: vision model execution, GPU memory optimisation, per-frame inference, optional batching.

### Video workloads (generation, effects, diffusion)

**Gateway** handles: stream orchestration, QoS and failover, output delivery, session lifecycle management.

**Orchestrator** handles: persistent GPU pipelines, multi-model composition, frame-by-frame generation, real-time conditioning.

### Text workloads (real-time only)

**Gateway** handles: request multiplexing, rate limiting, stable API surface.

**Orchestrator** handles: lightweight LLMs or classifiers, prompt routing and control logic, real-time response generation.

<CustomDivider />

## ASR pipeline examples

These are some of the best-fit workloads on Livepeer today.

### Live captions for video streams

```text icon="terminal" theme={null}
Mic / Video Audio
      ↓
Gateway (WebRTC audio chunks)
      ↓
Orchestrator (GPU ASR model)
      ↓
Incremental text tokens
      ↓
Gateway → captions / overlays / APIs
```

**Why it fits:** continuous audio stream, warm GPU state, incremental output, latency-critical UX.

### Multilingual live translation

```text icon="terminal" theme={null}
Live Audio
      ↓
ASR
      ↓
Translation model
      ↓
Translated captions (real-time)
```

**Why it fits:** chained streaming inference, strong latency requirements, high differentiation vs batch pipelines.

### Voice-driven avatars or agents

```text icon="terminal" theme={null}
Live Audio
      ↓
ASR
      ↓
Intent / command extraction
      ↓
Video or avatar pipeline conditioning
```

**Why it fits:** multimodal real-time control loop, audio conditions downstream video.

### Live moderation and safety

```text icon="terminal" theme={null}
Live Audio
      ↓
ASR
      ↓
Keyword / sentiment / policy model
      ↓
Flags, triggers, overlays
```

**Why it fits:** streaming classification, immediate downstream actions.

<CustomDivider />

## What about batch and file-based workloads?

<Accordion title="File-to-file and batch workloads: doable vs sensible">
  Livepeer will not block file-based or batch workloads. The protocol is general at the container level - anything that can run in a container can run on a Livepeer orchestrator. But Livepeer's economics, routing, and reliability are tuned for streaming inference, not batch conversion.

  **The precise rule:**

  > File-to-file is usually a bad fit - unless the conversion is actually streaming inference in disguise. Livepeer cares about **execution shape**, not inputs.

  **Your examples, explicitly:**

  **YouTube video → MP3**
  Doable. Bad idea. CPU-bound, no inference, long-running batch job, wastes GPU slots, will be deprioritised by gateways. Technically works. Economically irrational.

  **English → other language (translation)**

  * File-to-file (text in → text out): batch job, latency-tolerant - weak Livepeer fit.
  * Live translation (speech or captions): audio arrives incrementally, translation emitted incrementally, latency matters - excellent Livepeer fit.
    Same model, different execution shape.

  **MP3 → text transcription**

  * Upload MP3 → wait → download transcript: marginal. Works, but batch infra is cheaper and gateways gain little from routing it.
  * Streamed transcription (even from an MP3): chunk audio, emit tokens continuously, treat it like live audio - strong fit.

  **Reframe your mental model:**

  Stop thinking: "file → file = bad."
  Start thinking: "batch execution vs stream execution."

  | Task          | Batch  | Streaming    |
  | ------------- | ------ | ------------ |
  | MP3 → text    | ⚠ weak | ✓ strong     |
  | Translation   | ⚠ weak | ✓ strong     |
  | Video → audio | ✗ weak | ✗ still weak |
  | ASR           | ⚠ okay | ✓ excellent  |

  **The key constraint:**

  Livepeer's bottleneck is GPU opportunity cost, not capability. If a job occupies a GPU for a long time without benefiting from low latency, it will lose out to workloads that do. This is by design - gateways will naturally route away from poor-fit workloads.

  **Safe summary:**

  Many batch and file-based AI workloads are technically runnable on Livepeer. However, Livepeer is economically and operationally optimised for streaming, low-latency inference, and such workloads will be routed and priced accordingly.
</Accordion>

<CustomDivider />

## Next steps

<CardGroup cols={3}>
  <Card title="AI Pipelines" icon="diagram-project" href="/v2/developers/concepts/ai-on-livepeer">
    ComfyStream and BYOC - how to build and deploy inference pipelines.
  </Card>

  <Card title="BYOC" icon="box" href="/v2/developers/build/byoc">
    Bring your own container: run custom models on the network.
  </Card>

  <Card title="Model support" icon="microchip-ai" href="/v2/developers/build/model-support">
    Full compatibility matrix - which model families run on Livepeer.
  </Card>
</CardGroup>
