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

> ## Agent Instructions
> Unmute compiles to exactly three targets. Pipecat and LiveKit are code targets: compile writes a Python project you run. SLNG is a hosted target: compile writes a deployment body and SLNG runs the agent, so it has no `unmute dev`. Those three are the only values `provider` accepts in `targets.yaml`. Deepgram and ElevenLabs appear in these docs as model vendors, which is not the same thing as a target, and `slng` is both.
> The Go structs in `internal/spec` and `internal/ir` are the schema truth. Check a field against them, or run `unmute validate`, rather than against what you remember.

# Targets

> What a target is, what targets.yaml holds, and how one package compiles to two runtimes.

A target is where your agent runs. Unmute compiles one package into a native
project for the target you pick: Pipecat, LiveKit, or SLNG. Two are code
targets, and one is hosted.

**Pipecat** and **LiveKit** are code targets: `unmute compile` writes a
Python project you own, host and run.

**SLNG** is a hosted target: `unmute deploy` compiles a deployment body and
pushes it, and SLNG runs the agent. There is nothing to host and no
`unmute dev`.

Those three are the only values `provider` accepts.

On this page:

* [targets.yaml](#targets-yaml) - the file, and every key a target takes
* [Overrides, not forks](#overrides-not-forks) - changing one model entry for one target
* [Switching a package to another target](#switching-a-package-to-another-target) - the two edits that travel together
* [One package, two projects](#one-package-two-projects) - what `unmute compile` writes
* [Choosing between them](#choosing-between-them) - what usually decides it
* [Where to go next](#where-to-go-next) - each target's own page

## Targets YAML

```yaml targets.yaml theme={null}
targets:
  pipecat:
    provider: pipecat
    version: "1.10.0"

  livekit:
    provider: livekit
    version: "1.8.1"
    sdk_language: python
    models:
      detector:
        provider: livekit
        model: turn-detector-mini
```

The key (`pipecat`, `livekit`) is the **target instance name**. It is what you
pass to `--target`, and it becomes the directory name under `build/`. The name
is yours: a package with two Twilio setups might call them `pipecat_twilio` and
`pipecat_telnyx`.

### Every key a target takes

One is required everywhere. The rest depend on the provider you chose.

<ParamField path="provider" type="livekit | pipecat | slng" required>
  Which runtime this instance compiles for. Those three names are the whole
  list. `vapi` and `deepgram` were target names once, and both are now refused
  by name.
</ParamField>

<ParamField path="version" type="exact framework version">
  The framework release an emitted project pins, written with all three
  numbers. Required on `livekit` and `pipecat`. Refused on `slng`, which owns
  the version its agents run on.
</ParamField>

<ParamField path="sdk_language" type="python">
  The language an emitted project is written in. `python` is the only value
  either driver has templates for, and LiveKit needs it stated to compile an
  MCP tool. Refused on `slng`.
</ParamField>

<ParamField path="pins" type="package name to version">
  Package versions for the emitted LiveKit project, limited to names the
  LiveKit driver knows. Pipecat reads none of them, so leave it out there.
  Refused on `slng`.
</ParamField>

<ParamField path="connection" type="connection file stem">
  The one [connection file](/reference/connections-yaml) carrying this target's
  phone calls. Required once the package declares a phone channel on `livekit`
  or `pipecat`, and refused when nothing in the package uses a phone route.
  Refused on `slng`, which has no carrier state in a package.
</ParamField>

<ParamField path="deployment_region" type="string or list of strings">
  LiveKit accepts `us-east`, `eu-central`, or `ap-south`, singly or in a duplicate-free list.
  Pipecat forwards one non-empty platform region. Both use platform placement when omitted.
  SLNG requires one of its [13 world parts](/targets/slng#targets-yaml); no default is inferred.
</ParamField>

<ParamField path="warm_instances" type="integer, zero or more" default="0">
  Instances the platform holds ready, so the first call after a quiet period is
  not waiting for a container to start. Pipecat only: LiveKit and `slng` refuse
  a stated pool. Left out means none, and the platform scales to zero when idle.
</ParamField>

<ParamField path="models" type="model entry name to a model definition">
  Per target overrides of named entries from `agent.yaml`. An override replaces
  the entry rather than merging into it. See
  [Overrides, not forks](#overrides-not-forks).
</ParamField>

A target says nothing else about telephony. It names one connection, and
[the connection file](/reference/connections-yaml) declares the transport, the
carrier, and the environment names the route needs. The numbers a transfer dials live in
`agent.yaml` under `destinations:`, because who you escalate to is the same desk
whichever carrier reaches it.

## Overrides, not forks

A target that cannot run a model as defined overrides that entry by name:

```yaml theme={null}
    models:
      detector:
        provider: livekit
        model: turn-detector-mini
```

The agent does not change. One entry does. This is the shape of every per
target difference, and it is why a package can serve both runtimes without a
second copy of anything.

## Switching a package to another target

`unmute init` scaffolds a **LiveKit** package: `targets.yaml` holds one
`livekit` instance, and `agent.yaml` carries the turn detector that goes with
it. Moving that package to Pipecat, or a Pipecat package to LiveKit, is **two
edits, not one**. The target and the turn model travel together.

This is the `turn:` block in `agent.yaml`, written for each one:

<CodeGroup>
  ```yaml LiveKit theme={null}
  models:
    turn:
      detector:
        provider: livekit
        model: turn-detector-mini
  ```

  ```yaml Pipecat theme={null}
  models:
    turn:
      detector:
        provider: local
        model: silero
  ```
</CodeGroup>

Point a Pipecat package at LiveKit and leave `silero` behind, and validate
refuses before a file is written:

```text theme={null}
✗ livekit (livekit)

Errors:
  livekit: turn model "silero" is not recognized; use turn-detector-mini (local) or turn-detector (LiveKit Cloud)
```

The other direction does not refuse. Pipecat forwards the turn binding as
written and runs Silero either way, so a LiveKit turn model left in a Pipecat
package is not an error, just a line of YAML that says something the generated
project does not do. Fix it anyway.

If you want to keep both targets rather than swap one for the other, do not
edit `agent.yaml` at all. Leave the Pipecat binding in place and give the
LiveKit instance the per target `models:` override shown above, which is what
every shipped example does.

## One package, two projects

```sh theme={null}
unmute compile examples/salon-concierge
```

```text theme={null}
generated examples/salon-concierge/build/livekit/agent.py
generated examples/salon-concierge/build/pipecat/bot.py
```

Each directory is a complete project: source, pinned dependencies, Dockerfile,
`.env.example`, and a runbook README written for that platform.

`--target` limits the work:

```sh theme={null}
unmute compile examples/salon-concierge --target pipecat
```

## Choosing between them

Both run the same agent. The differences that usually decide it:

|                                | Pipecat                                                                      | LiveKit                                  |
| ------------------------------ | ---------------------------------------------------------------------------- | ---------------------------------------- |
| generated entry point          | `bot.py`                                                                     | `agent.py`                               |
| phone routes                   | your carrier through Daily or over websockets, Pipecat Cloud carrier streams | SIP trunks, or a generated Twilio bridge |
| transfers                      | cold, on two of its routes                                                   | cold and warm, on the SIP route          |
| turn detection in the examples | local Silero                                                                 | LiveKit's own turn model                 |
| provider lists                 | see [Models](/models/stt)                                                    | see [Models](/models/stt)                |

Running the generated project yourself differs too:

<CodeGroup>
  ```sh Pipecat theme={null}
  uv run bot.py -t webrtc
  # or: unmute dev, which runs it under uv
  ```

  ```sh LiveKit theme={null}
  uv run python -m livekit.agents start agent.py
  # or: unmute dev, which runs it in Docker against a local LiveKit server
  ```
</CodeGroup>

If you need warm transfer today, that is the LiveKit SIP route. If you want a
phone number with nothing hosted, that is one of the Pipecat routes. For a
browser agent, either.

## Where to go next

<CardGroup cols={3}>
  <Card title="The Pipecat project" icon="boxes" href="/targets/pipecat">
    What `build/pipecat/` contains.
  </Card>

  <Card title="The LiveKit project" icon="boxes" href="/targets/livekit">
    What `build/livekit/` contains.
  </Card>

  <Card title="The SLNG deployment body" icon="cloud" href="/targets/slng">
    What `build/slng/` contains, and what SLNG runs for you.
  </Card>
</CardGroup>
