> ## 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.

<Note>
  Historical migration: Python 2.5 to 3.0 (core 0.21 to 0.22). For a current upgrade, start with the [upgrade guide](/reference/sdk/upgrading) and [compatibility matrix](/reference/sdk/compatibility-matrix).
</Note>

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>
  No key regeneration is needed for this upgrade. Re-download compatible model files: core 0.21 uses file format 5, while core 0.22 uses format 6. Keep the previous files separately if you need to roll back.
</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)` preserves the `float32` dtype used here. The explicit cast also handles inputs such as integers, whose mean defaults to `float64`. `process()` requires a contiguous array; use `np.ascontiguousarray` for non-contiguous slices.
</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()
```

Stop submitting audio before termination and treat the object as closed afterward. Processing can become disallowed asynchronously; the method returning does not guarantee immediate cutoff. Native termination may block while the final session drains. Destruction also terminates the session, but explicit termination avoids waiting for garbage collection.

## 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.