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

# integrations.yml

> The LLM provider, input channels, optional model groups and Langfuse tracing.

`integrations.yml` is required at the project root. It declares the LLM that
drives the agent, the channels customers reach it through, and any named model
groups.

The file is copied into the model archive at `rasa train`, and each section is
read from the place that suits what it configures:

| Section        | Read from                  | Effect                                                                  |
| -------------- | -------------------------- | ----------------------------------------------------------------------- |
| `llm`          | The live project directory | Change the provider or model and restart. No retraining.                |
| `channels`     | The live project directory | Change channel settings and restart.                                    |
| `model_groups` | The packaged snapshot      | The embedder that built the reference index is the one that queries it. |
| `tracing`      | The live project directory | Optional Langfuse observability; restart to pick up changes.            |

`model_groups` is the one to know about. Editing it in your project after
training does not change how references are searched, because the query
embeddings have to match the vectors built at train time. Retrain to pick up a
new embedding model.

```yaml integrations.yml theme={null}
llm:
  provider: openai
  model: gpt-5.1
  api_base: https://api.openai.com/v1
  api_key_env: OPENAI_API_KEY

channels:
  rest: { enabled: true }
  inspector: { enabled: true }

# Optional — Langfuse tracing (often left commented until keys are set)
# tracing:
#   type: langfuse
#   public_key: ${LANGFUSE_PUBLIC_KEY}
#   private_key: ${LANGFUSE_SECRET_KEY}
#   host: https://cloud.langfuse.com
```

That is a complete, working file. Everything else is optional.

## `llm`

**Required.** One flat block describing the single model that drives the agent.

| Key          | Required | Description                                                         |
| ------------ | -------- | ------------------------------------------------------------------- |
| `provider`   | **Yes**  | Provider id (`openai`, `anthropic`, `azure`, …). Must be non-empty. |
| `model`      | Yes\*    | Model name.                                                         |
| `deployment` | Yes\*    | Azure OpenAI deployment name, used **instead of** `model`.          |

\* Exactly one of `model` or `deployment` must be set. A section with neither
fails validation with `calm_v2.validation.config.missing_provider`.

Every other key is passed to the provider client: `api_base`, `temperature`,
`timeout`, and so on. Two keys are handled by Maestro before that happens:

| Key                 | Handling                                                                      |
| ------------------- | ----------------------------------------------------------------------------- |
| `api_key_env`       | Read as the **name** of an environment variable and resolved into the API key |
| `max_prompt_tokens` | Consumed by prompt construction as the token budget for the assembled prompt  |

`provider` sits at the top level of this block, which is what makes it a flat
mapping. The agent's main LLM is always configured here, never through
`model_groups`.

### Provider examples

`provider: openai`, `azure`, and `self-hosted` have dedicated clients. Any other
value is passed through to LiteLLM, so a provider LiteLLM supports works by
naming it and its model.

**OpenAI**

```yaml integrations.yml theme={null}
llm:
  provider: openai
  model: gpt-5.1
  api_key_env: OPENAI_API_KEY

channels:
  rest: { enabled: true }
```

**Anthropic**

```yaml integrations.yml theme={null}
llm:
  provider: anthropic
  model: claude-sonnet-4-5-20250929
  api_key_env: ANTHROPIC_API_KEY

channels:
  rest: { enabled: true }
```

**Google Gemini**

```yaml integrations.yml theme={null}
llm:
  provider: gemini
  model: gemini-2.5-pro
  api_key_env: GEMINI_API_KEY

channels:
  rest: { enabled: true }
```

**Azure OpenAI.** Name the `deployment` rather than the model, and give the
endpoint and API version:

```yaml integrations.yml theme={null}
llm:
  provider: azure
  deployment: my-gpt-deployment
  api_base: https://my-resource.openai.azure.com/
  api_version: "2024-10-21"
  api_key_env: AZURE_OPENAI_API_KEY

channels:
  rest: { enabled: true }
```

**Self-hosted, OpenAI-compatible.** For vLLM, Ollama, TGI, or anything else
serving the OpenAI API shape. `provider`, `model`, and `api_base` are all
required:

```yaml integrations.yml theme={null}
llm:
  provider: self-hosted
  model: mistralai/Mistral-7B-Instruct-v0.3
  api_base: http://localhost:8000/v1
  api_key_env: LOCAL_LLM_API_KEY

channels:
  rest: { enabled: true }
```

`model` is the name your server advertises, and `api_base` points at the
OpenAI-compatible route, usually ending in `/v1`. Drop `api_key_env` if the
server takes no key.

### Secrets

`api_key_env` names an environment variable and works for every provider:
Maestro resolves it into the API key before the config reaches the client.

`${VAR}` expansion happens in the `channels:` block, which is read with
environment variables expanded. `llm:` and `model_groups:` are read without
expansion, so `api_key_env` is the mechanism there. Never commit a literal key.

## `channels`

Declares the channels customers reach the agent through.

```yaml theme={null}
channels:
  rest: { enabled: true }
  inspector: { enabled: true }
```

Each key is a channel name from the built-in channel registry. Values may be:

| Form                                   | Meaning                                       |
| -------------------------------------- | --------------------------------------------- |
| `{ enabled: true }`                    | Enable with defaults                          |
| `{ enabled: true, <key>: <value>, … }` | Enable with those keyword arguments           |
| `{ enabled: false }`                   | Disabled: kept in the file but not registered |
| *(empty / null)*                       | Enable with defaults                          |

The `enabled` flag is stripped before the remaining keys are handed to the
channel. A non-boolean `enabled`, or an entry that is neither a mapping nor
empty, raises at load.

<Note>
  `rest` and `inspector` are the practical minimum: `rest` is what the evaluation
  runner talks to, and `inspector` is required for `rasa inspect`. A project with
  no `channels:` block registers no channels at all.
</Note>

`${VAR}` references inside `channels:` are expanded when the file is read, so
channel credentials can come from the environment.

### Voice channels

A voice channel takes `asr:` and `tts:` sub-mappings, and that is where speech
recognition and synthesis are configured. Each takes a `name:` selecting the
engine, plus that engine's own settings:

```yaml integrations.yml theme={null}
llm:
  provider: openai
  model: gpt-5.1
  api_key_env: OPENAI_API_KEY

channels:
  browser_audio:
    enabled: true
    server_url: localhost:5005
    asr:
      name: deepgram
      language_map:
        en-US:
          language: en
          model: flux-general-en
    tts:
      name: deepgram
      language_map:
        en-US:
          model: aura-2-asteria-en
```

| Engine     | Built-in names                          |
| ---------- | --------------------------------------- |
| `asr.name` | `deepgram`, `azure`                     |
| `tts.name` | `deepgram`, `azure`, `cartesia`, `rime` |

Every key besides `name` goes to that engine, so the available settings are the
engine's own.

`language_map` keys the model and language off the conversation language, which
is how both engines take their settings. `name` also accepts a Python module
path, which loads a custom engine implementing `from_config_dict`.

#### Deepgram Flux for speech recognition

Flux is the model family to reach for on a live voice agent. It does turn
detection inside the ASR rather than inferring it from silence, so the agent
takes its turn on the signal the recogniser already has.

Naming a `flux-` model selects it. The engine reads the model name out of
`language_map` and switches to the Flux API on its own, so there is nothing else
to declare:

```yaml integrations.yml theme={null}
llm:
  provider: openai
  model: gpt-5.1
  api_key_env: OPENAI_API_KEY

channels:
  browser_audio:
    enabled: true
    server_url: localhost:5005
    asr:
      name: deepgram
      language_map:
        en-US:
          language: en
          model: flux-general-en
      eot_threshold: 0.8
      eot_timeout_ms: 5000
    tts:
      name: deepgram
      language_map:
        en-US:
          model: aura-2-asteria-en
```

The two turn-detection knobs are optional:

| Key              | Range        | Default | Effect                                                                            |
| ---------------- | ------------ | ------- | --------------------------------------------------------------------------------- |
| `eot_threshold`  | 0.5 to 0.9   | 0.7     | Confidence needed to call end of turn. Lower responds faster and interrupts more. |
| `eot_timeout_ms` | 500 to 10000 | 5000    | Ceiling on silence before the turn is ended regardless.                           |

A Flux config takes these two keys and no others. Anything unrecognised fails
the load rather than being ignored, so a typo surfaces at startup.

The same `asr:` / `tts:` shape applies to every voice channel, including
`jambonz`, `audiocodes`, `twilio_media_streams`, and `genesys`. What differs
between them is the telephony connection: `server_url`, and any credentials that
provider needs.

## `model_groups`

Optional. Named model configurations, used today only by the references
embedder.

```yaml theme={null}
model_groups:
  - id: reference_embeddings
    models:
      - provider: openai
        model: text-embedding-3-large
```

Point `agent.yml` at one by id:

```yaml agent.yml theme={null}
agent:
  persona: You are a helpful assistant.

references:
  embeddings: reference_embeddings
```

`references.embeddings` must name a group declared here. Naming an undeclared
group fails the index build at `rasa train` with a message saying so.

## Validation

`rasa train` and `rasa data validate` check this file:

| Code                                             | Cause                                                           |
| ------------------------------------------------ | --------------------------------------------------------------- |
| `calm_v2.validation.config.missing_integrations` | No `integrations.yml` at the project root                       |
| `calm_v2.validation.config.missing_llm`          | No `llm:` section, or it is not a mapping                       |
| `calm_v2.validation.config.missing_provider`     | `llm:` lacks `provider`, or lacks both `model` and `deployment` |
| `calm_v2.validation.config.invalid_tracing`      | Invalid or unsupported `tracing:` section                       |

## Tracing (Langfuse)

Maestro projects configure Langfuse under `tracing:` in `integrations.yml`.

```yaml theme={null}
tracing:
  type: langfuse
  public_key: ${LANGFUSE_PUBLIC_KEY}
  private_key: ${LANGFUSE_SECRET_KEY}
  host: https://cloud.langfuse.com
```

| Key                         | Required | Notes                                                                 |
| --------------------------- | -------- | --------------------------------------------------------------------- |
| `type`                      | yes      | Must be `langfuse`.                                                   |
| `public_key`                | yes      | Must use a `${ENV_VAR}` reference — literal secrets are rejected.     |
| `private_key`               | yes      | Must use a `${ENV_VAR}` reference — literal secrets are rejected.     |
| `host`                      | yes      | Langfuse Cloud or your self-hosted URL.                               |
| `environment`               | no       | Optional Langfuse environment name (lowercase, digits, `-`/`_`).      |
| `timeout`                   | no       | HTTP timeout in seconds (maps to `LANGFUSE_TIMEOUT`).                 |
| `debug`                     | no       | Enable Langfuse debug logging (`LANGFUSE_DEBUG`).                     |
| `release`                   | no       | Release/version tag attached to traces (`LANGFUSE_RELEASE`).          |
| `sample_rate`               | no       | Trace sampling rate between 0 and 1 (`LANGFUSE_SAMPLE_RATE`).         |
| `media_upload_thread_count` | no       | Media upload thread pool size (`LANGFUSE_MEDIA_UPLOAD_THREAD_COUNT`). |

`public_key` and `private_key` must use `${ENV_VAR}` references — literal secrets
are rejected at validation and apply time. Those references are kept as literal
strings when `integrations.yml` is parsed (they are not expanded like channel
credentials). They are resolved when Langfuse configures at CLI startup; if a
variable is unset, resolution is best-effort and may leave the literal `${VAR}`
in the process environment, so tracing is skipped rather than aborting startup.
Use `rasa data validate` to catch invalid shape or key syntax before deploy.

Install the monitoring extra (`pip install rasa-pro[monitoring]`), set
`LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY`, then enable the block. Example
agents ship it commented out so local defaults stay key-free.

## See also

* [`agent.yml`](/reference/agent-yml): identity, persona, prompt tuning
