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

# Migrate Python SDK 2.5 to 3.0

> Upgrade to ai-coustics Python SDK 3.0 with mono-only processing, a dedicated Vad class, and renamed configuration fields and errors.

Python SDK 3.0 tracks core SDK 0.22.0. It contains two breaking changes that affect every integration, plus a set of renames.

<CardGroup cols={2}>
  <Card title="Audio is mono only" href="/reference/deprecated/multi-channel-to-mono">
    `process()` and `buffer()` take a 1D array. The internal mixdown is gone.
  </Card>

  <Card title="VAD is its own object" href="/reference/deprecated/energy-vad-to-dedicated-vad">
    `Vad` runs a dedicated VAD model. Energy-based VAD is removed.
  </Card>
</CardGroup>

This page is the Python-specific reference. The two guides above explain the reasoning and the audio-handling changes in more detail.

<Warning>
  Your license key and your enhancement models keep working. No key regeneration is needed for this upgrade.
</Warning>

## Quick migration checklist

<Steps>
  <Step title="Downmix to mono">
    Pass a 1D `float32` array to `process()`, `process_async()`, and `buffer()`. A 2D array now raises `AudioConfigMismatchError`.
  </Step>

  <Step title="Rename the config fields">
    `num_frames` is `block_size`, `allow_variable_frames` is `variable_block_size`, and `num_channels` is gone.
  </Step>

  <Step title="Rename get_processor_context">
    It is now `get_context()` on both `Processor` and `ProcessorAsync`.
  </Step>

  <Step title="Replace the processor-owned VAD">
    Create a `Vad` or `VadAsync` from a dedicated VAD model. `get_vad_context()` no longer exists.
  </Step>

  <Step title="Update the three renamed error classes">
    `NotInitializedError`, `ProcessingNotAllowedError`, and `FilePathInvalidError`.
  </Step>

  <Step title="Keep model types in the right objects">
    `Processor` takes enhancement and bypass models only. `Vad` takes VAD models only.
  </Step>
</Steps>

## Renames

### `ProcessorConfig`

`ProcessorConfig` is shared by `Processor`, `Vad`, and `Collector`.

| 2.5                                                                             | 3.0                                                             |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------- |
| `ProcessorConfig(sample_rate, num_channels, num_frames, allow_variable_frames)` | `ProcessorConfig(sample_rate, block_size, variable_block_size)` |
| `config.num_frames`                                                             | `config.block_size`                                             |
| `config.allow_variable_frames`                                                  | `config.variable_block_size`                                    |
| `config.num_channels`                                                           | Removed                                                         |
| `ProcessorConfig.optimal(model, num_channels=2)`                                | `ProcessorConfig.optimal(model)`                                |

<CodeGroup>
  ```python 2.5 theme={null}
  config = aic.ProcessorConfig(
      sample_rate=48000,
      num_channels=2,
      num_frames=480,
      allow_variable_frames=False,
  )
  ```

  ```python 3.0 theme={null}
  config = aic.ProcessorConfig(
      sample_rate=48000,
      block_size=480,
      variable_block_size=False,
  )
  ```
</CodeGroup>

### `Model`

| 2.5                                         | 3.0                                         |
| ------------------------------------------- | ------------------------------------------- |
| `model.get_optimal_num_frames(sample_rate)` | `model.get_optimal_block_size(sample_rate)` |

### `Processor` and `ProcessorAsync`

| 2.5                                        | 3.0                                             |
| ------------------------------------------ | ----------------------------------------------- |
| `processor.get_processor_context()`        | `processor.get_context()`                       |
| `processor.get_vad_context()`              | Removed, create a `Vad` instead                 |
| `processor.process(audio)` with a 2D array | `processor.process(audio)` with a 1D mono array |
| (not available)                            | `processor.terminate_session()`                 |

### `ProcessorContext`

| 2.5                           | 3.0                          |
| ----------------------------- | ---------------------------- |
| `proc_ctx.get_output_delay()` | `proc_ctx.get_audio_delay()` |

The rename says what the value delays. `get_audio_delay()` reports how far the enhanced samples lag their input. It no longer covers VAD timing, because the VAD has its own `get_prediction_delay()`.

`ProcessorContext.reset()` now affects enhancement state only. Reset a VAD through its own `VadContext`.

### Errors

| 2.5                          | 3.0                         |
| ---------------------------- | --------------------------- |
| `ModelNotInitializedError`   | `NotInitializedError`       |
| `EnhancementNotAllowedError` | `ProcessingNotAllowedError` |
| `ModelFilePathInvalidError`  | `FilePathInvalidError`      |

## Migrate enhancement

<CodeGroup>
  ```python 2.5 theme={null}
  import aic_sdk as aic
  import numpy as np

  model = aic.Model.from_file(model_path)
  config = aic.ProcessorConfig.optimal(model, num_channels=2)
  processor = aic.Processor(model, license_key, config)

  proc_ctx = processor.get_processor_context()
  print(f"Delay: {proc_ctx.get_output_delay()} samples")

  # Stereo in, stereo out. The SDK mixed both channels down internally.
  audio = np.zeros((config.num_channels, config.num_frames), dtype=np.float32)
  enhanced = processor.process(audio)
  ```

  ```python 3.0 theme={null}
  import aic_sdk as aic
  import numpy as np

  model = aic.Model.from_file(model_path)
  config = aic.ProcessorConfig.optimal(model)
  processor = aic.Processor(model, license_key, config)

  proc_ctx = processor.get_context()
  print(f"Delay: {proc_ctx.get_audio_delay()} samples")

  # Mono in, mono out. Downmix in your application first.
  audio = np.zeros(config.block_size, dtype=np.float32)
  enhanced = processor.process(audio)
  ```
</CodeGroup>

To downmix a file loaded with `soundfile`:

```python theme={null}
import numpy as np
import soundfile as sf

audio, sample_rate = sf.read(path, dtype="float32")

# audio is (frames,) for mono or (frames, channels) for multi-channel.
if audio.ndim > 1:
    audio = audio.mean(axis=1)

audio = np.ascontiguousarray(audio, dtype=np.float32)
```

<Note>
  `mean(axis=1)` widens to `float64` for many input dtypes, so cast back to `float32`. `process()` also needs a contiguous array, which is why slices of a larger buffer may need `np.ascontiguousarray`.
</Note>

To keep channels separate, create one `Processor` per channel. Each one holds the state for exactly one stream.

## Migrate VAD

<CodeGroup>
  ```python 2.5 theme={null}
  # The VAD came from the enhancement processor.
  processor = aic.Processor(model, license_key, config)
  vad_ctx = processor.get_vad_context()
  vad_ctx.set_parameter(aic.VadParameter.Sensitivity, 5.0)  # energy threshold

  enhanced = processor.process(audio)  # also advanced the VAD

  if vad_ctx.is_speech_detected():
      print("Speech detected")
  ```

  ```python 3.0 theme={null}
  # The VAD is its own object, backed by a dedicated VAD model.
  vad_model = aic.Model.from_file(
      aic.Model.download("vad-2.1-xxs-16khz", "./models")
  )
  vad = aic.Vad(vad_model, license_key, aic.ProcessorConfig.optimal(vad_model))
  vad_ctx = vad.get_context()
  vad_ctx.set_parameter(aic.VadParameter.Sensitivity, 0.8)  # probability

  vad.process(audio)  # returns None, does not modify the audio

  if vad_ctx.is_speech_detected():
      print("Speech detected")
  ```
</CodeGroup>

<Warning>
  `VadParameter.Sensitivity` is now a probability threshold from `0.0` to `1.0`, and the direction is inverted: higher values require more confidence and therefore fire less often. Old energy-threshold values above `1.0` raise `ParameterOutOfRangeError`. Retune against real audio and start from the model default. See [Retune sensitivity](/reference/deprecated/energy-vad-to-dedicated-vad#retune-sensitivity).
</Warning>

Running both together, on the same original input block:

```python theme={null}
vad.process(audio)                 # reads the block
enhanced = processor.process(audio) # returns the enhanced block
```

Feed the VAD the original input rather than the enhanced output. Enhancement changes the signal the VAD model was trained on, and it adds the processor's delay on top of the VAD's own.

### Async VAD

`VadAsync` mirrors `ProcessorAsync`:

```python theme={null}
vad = aic.VadAsync(vad_model, license_key)
await vad.initialize_async(config)
await vad.process_async(audio)

print(vad.get_context().is_speech_detected())
await vad.terminate_session_async()
```

### `VadContext`

`VadContext` gained the methods the processor context already had:

| Method                       | Notes                                                                                                       |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `is_speech_detected()`       | Unchanged                                                                                                   |
| `raw_vad_probability()`      | Unchanged. The model's probability before thresholding, useful for picking a sensitivity value              |
| `get_prediction_delay()`     | New on the VAD. How far the published prediction lags its input, independent of the processor's audio delay |
| `reset()`                    | New. Clears VAD state and immediately clears the published decision and probability                         |
| `update_bearer_token(token)` | New. Same JWT rules as the processor context                                                                |

<Info>
  These names match the C API's `aic_processor_context_get_audio_delay` and `aic_vad_context_get_prediction_delay`.
</Info>

## Model types are enforced

| Object                        | Accepts                       |
| ----------------------------- | ----------------------------- |
| `Processor`, `ProcessorAsync` | Enhancement and bypass models |
| `Vad`, `VadAsync`             | Dedicated VAD models          |
| `Analyzer`, `FileAnalyzer`    | Analysis models               |

A mismatch raises `ModelTypeUnsupportedError` at creation time.

## Session termination

`Processor`, `Vad`, and `Analyzer` can now close their telemetry session explicitly, instead of waiting for garbage collection:

```python theme={null}
processor.terminate_session()
vad.terminate_session()

# Async variants
await processor.terminate_session_async()
await vad.terminate_session_async()
```

The object cannot process more audio afterwards. The session is still terminated automatically on destruction, so this is only needed where deallocation may be delayed.

## Validation

* Confirm every `process()`, `process_async()`, and `buffer()` call receives a contiguous 1D `float32` array.
* Confirm no code still reads `config.num_frames`, `config.num_channels`, or `config.allow_variable_frames`.
* Search for `get_processor_context` and `get_vad_context`.
* Search for the three renamed error classes, including in `except` clauses.
* Compare speech detection against representative audio after retuning sensitivity.
* Compare enhancement output level against representative recordings if you previously relied on the internal mixdown and mixback.

## Need help?

* See the [GitHub repository](https://github.com/ai-coustics/aic-sdk-py) and the [type stubs file](https://github.com/ai-coustics/aic-sdk-py/blob/main/aic_sdk.pyi).
* Browse available models at [artifacts.ai-coustics.io](https://artifacts.ai-coustics.io).
* Check the [compatibility matrix](/reference/sdk/compatibility-matrix) for binding, core, and model file versions.
