> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Orchestrator Selection and Pricing

> How gateways select orchestrators for transcoding and AI inference jobs: stake-weighted selection, price filtering, and capability matching.

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </div>;
};

export const CenteredContainer = ({children, maxWidth = "800px", padding = "0", preset = "default", width = "", minWidth = "", marginRight = "", marginBottom = "", textAlign = "", style = {}, className = "", ...rest}) => {
  const presets = {
    default: {},
    fitContent: {
      width: "fit-content",
      minWidth: "fit-content"
    },
    readable70: {
      width: "70%",
      minWidth: "fit-content"
    },
    readable80: {
      width: "80%",
      minWidth: "fit-content"
    },
    readable90: {
      width: "90%"
    },
    wide900: {
      maxWidth: "900px"
    }
  };
  const presetStyle = presets[preset] || presets.default;
  return <div className={className} style={{
    maxWidth: presetStyle.maxWidth || maxWidth,
    margin: "0 auto",
    padding: padding,
    ...presetStyle.width ? {
      width: presetStyle.width
    } : {},
    ...presetStyle.minWidth ? {
      minWidth: presetStyle.minWidth
    } : {},
    ...width ? {
      width
    } : {},
    ...minWidth ? {
      minWidth
    } : {},
    ...marginRight ? {
      marginRight
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...textAlign ? {
      textAlign
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

export const LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

<CenteredContainer preset="readable90">
  <Tip>Orchestrator selection is Gateway-side. The Gateway queries active Orchestrators, filters by price and capability, then applies stake-weighted selection. Orchestrators with higher stake and lower latency win more work.</Tip>
</CenteredContainer>

***

When a Gateway receives a job (a transcoding segment or an AI inference request), it selects an Orchestrator from the Active Set to process the work. Selection combines three criteria: capability matching (does the Orchestrator support the requested pipeline and model?), price filtering (is the Orchestrator's price within the Gateway's maximum?), and stake-weighted random selection among qualifying candidates.

<CustomDivider />

## Capability Matching

For AI inference jobs, the Gateway matches against Orchestrators registered in the AI Service Registry on Arbitrum One. The registry records which pipelines and model IDs each Orchestrator advertises as warm. A Gateway requesting `text-to-image` with model `ByteDance/SDXL-Lightning` only considers Orchestrators advertising that combination.

Capability data is read at session start; the Gateway caches the Active Set and refreshes periodically. Cold capabilities (advertised but not currently loaded) are visible in the registry but may result in longer job latency as the Orchestrator loads the model.

## Price Filtering

Orchestrators advertise a price per unit. The Gateway filters out any Orchestrator whose price exceeds `-maxPricePerUnit`:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer -broadcaster \
  -maxPricePerUnit 1000000000000000 \   # max 0.001 ETH/unit
  ...
```

For transcoding, the unit is wei per pixel. For AI batch pipelines, the unit is pipeline-specific (per output, per second, or per request depending on Orchestrator configuration). Orchestrators advertising a price of 0 accept any work regardless of the Gateway's maximum.

Setting `-maxPricePerUnit 0` disables price filtering: the Gateway accepts Orchestrators at any price.

## Stake-Weighted Selection

Among Orchestrators that pass capability and price filters, the Gateway applies stake-weighted random selection. An Orchestrator with twice the staked LPT of another has approximately twice the probability of being selected for a given job, holding other factors equal.

The discovery ping time also influences selection: Orchestrators that consistently fail to respond or return errors are removed from the active selection pool until they recover.

This mechanism is stable for the duration of a stream: once a session is assigned to an Orchestrator, the Gateway continues routing that session's segments to the same Orchestrator unless it fails.

<CustomDivider />

## Restricting to Specific Orchestrators

From go-livepeer PR #3634, Gateway clients can include or exclude specific Orchestrators by `serviceAddr` in their requests:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
# Include only specific orchestrators
livepeer -broadcaster \
  -orchAddr "https://orch-1.example.com,https://orch-2.example.com" \
  ...
```

This is useful for operators running their own Orchestrators, for testing, or for agreements between Gateway and Orchestrator operators outside the open market.

## Viewing Active Orchestrators

The Livepeer Explorer at `explorer.livepeer.org` shows the active Orchestrator set with stake, price per pixel, and performance scores. The AI capability dashboard at `tools.livepeer.cloud/ai/network-capabilities` shows AI-specific warm capabilities.

<CustomDivider />

## Setting Prices as an Orchestrator

Orchestrators set pricing per pipeline type. For transcoding:

```bash theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer -orchestrator \
  -pricePerUnit 1000  # wei per pixel
  ...
```

For AI batch pipelines, pricing is set in `aiModels.json` per pipeline entry:

```json theme={"theme":{"light":"github-light","dark":"dark-plus"}}
{
  "pipeline": "text-to-image",
  "model_id": "ByteDance/SDXL-Lightning",
  "price_per_unit": 5000000000000000,
  "warm": true
}
```

For `live-video-to-video`, `price_per_unit` is in wei per second.

Competitive pricing relative to the Gateway's `-maxPricePerUnit` determines whether an Orchestrator enters the selection pool at all. Orchestrators priced above the Gateway's maximum receive no work from that Gateway regardless of stake.

<CustomDivider />

## Market Dynamics

The Active Set posts prices on-chain via the AI Service Registry and via the off-chain `GetOrchestratorInfo` RPC. Gateways negotiate directly during session establishment.

The Livepeer Explorer shows historical price data per Orchestrator. The `tools.livepeer.cloud/ai/network-capabilities` dashboard shows current warm AI capabilities but does not expose per-Orchestrator pricing.

<CustomDivider />

## Related Pages

<CardGroup cols={2}>
  <Card title="Probabilistic Micropayments" icon="coin" href="/v2/developers/guides/payments/probabilistic-micropayments" arrow horizontal>
    How tickets are priced and the per-unit model used in selection.
  </Card>

  <Card title="ETH Escrow and Deposits" icon="lock" href="/v2/developers/guides/payments/eth-escrow-and-deposits" arrow horizontal>
    Funding the Gateway deposit that backs PM tickets sent to Orchestrators.
  </Card>

  <Card title="BYOC Overview" icon="box" href="/v2/developers/build/compute/byoc/overview" arrow horizontal>
    Registering custom capabilities that influence Orchestrator selection.
  </Card>

  <Card title="Alt-Gateway Overview" icon="server" href="/v2/developers/build/alt-gateways/overview" arrow horizontal>
    Off-chain Gateway clients and their Orchestrator discovery patterns.
  </Card>
</CardGroup>
