# Plugin system (/configuration/plugins)



## Overview

Each QVAC AI capability maps to a **built-in plugin** in the SDK. This lets you enable only what you need for your project and reduce your application's final bundle size.

In addition, you can add **custom plugins** — both your own and community ones — that extend QVAC's capabilities. In both cases, you'll use the QVAC configuration file and then bundle either with [QVAC CLI](/cli) or [programmatically via `@qvac/sdk/commands`](#notes).

## Built-in plugins

### Catalog

All the built-in plugins you can select, along with the AI tasks that depend on each one:

| Plugin                             | Use in `qvac.config.*`                          | AI tasks that require it                                                                                                                                                  |
| ---------------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| LLM                                | `@qvac/sdk/llamacpp-completion/plugin`          | [Text generation](/ai-capabilities/text-generation); [multimodal](/ai-capabilities/multimodal); [RAG](/ai-capabilities/rag) ; [Fine-tuning](/ai-capabilities/fine-tuning) |
| Embeddings                         | `@qvac/sdk/llamacpp-embedding/plugin`           | [Text embeddings](/ai-capabilities/text-embeddings); [RAG](/ai-capabilities/rag)                                                                                          |
| ASR with customized Whisper engine | `@qvac/sdk/whispercpp-transcription/plugin`     | [Transcription](/ai-capabilities/transcription)                                                                                                                           |
| ASR with Parakeet                  | `@qvac/sdk/parakeet-transcription/plugin`       | [Transcription](/ai-capabilities/transcription)                                                                                                                           |
| BCI transcription                  | `@qvac/sdk/bci-whispercpp-transcription/plugin` | [Transcription](/ai-capabilities/transcription#bci-neural-signal-transcription)                                                                                           |
| BCI transcription                  | `@qvac/sdk/bci-whispercpp-transcription/plugin` | [Transcription](/ai-capabilities/transcription#bci-neural-signal-transcription)                                                                                           |
| NMT                                | `@qvac/sdk/nmtcpp-translation/plugin`           | [Translation](/ai-capabilities/translation)                                                                                                                               |
| TTS                                | `@qvac/sdk/onnx-tts/plugin`                     | [Text-to-Speech](/ai-capabilities/text-to-speech)                                                                                                                         |
| OCR                                | `@qvac/sdk/onnx-ocr/plugin`                     | [OCR](/ai-capabilities/ocr)                                                                                                                                               |
| Diffusion                          | `@qvac/sdk/sdcpp-generation/plugin`             | [Image generation](/ai-capabilities/image-generation)                                                                                                                     |

### Enabling

<Steps>
  <Step>
    [In your `qvac.config.*`, add the built-in plugins you’ll need in your project](/configuration). For example:

    ```json title="qvac.config.json"
    {
      "plugins": [
        "@qvac/sdk/llamacpp-completion/plugin",
        "@qvac/sdk/onnx-ocr/plugin"
      ]
    }
    ```
  </Step>

  <Step>
    <Tabs>
      <Tab value="desktop" label="Node.js/Bare" default>
        When developing for desktop environment, use the QVAC CLI to (re)bundle the SDK only with the selected plugins:

        ```bash
        qvac bundle sdk
        ```
      </Tab>

      <Tab value="mobile" label="Expo">
        [When developing for mobile environment, use the Expo CLI to prebuild your project only with the selected plugins:](/installation/#expo)

        ```bash
        npx expo prebuild
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

Only the selected plugins are included in your bundle, significantly reducing your application size. If `plugins` is omitted or an empty array, it bundles all built-in plugins by default.

<Callout type="success">
  **Tips:**

  * You **do not need** the CLI (`@qvac/cli`) to bundle the SDK. See [Notes](#notes) to learn how to do it programmatically via `@qvac/sdk/commands`.
  * See [QVAC CLI](/cli) to learn how to install and use it.
  * When developing an Electron app, you can bundle the SDK during packaging via Electron Forge. See [Tutorials → Electron → Step 4: package for distribution](/tutorials/electron#step-4-package-for-distribution).
</Callout>

## Custom plugins

Custom plugins are consumed as npm packages. Install the package, enable it in your `qvac.config.*` file like any built-in plugin, then import and use its API alongside the SDK's regular API.

### Enabling

<Steps>
  <Step>
    Install the plugin package in your project. For example:

    ```bash
    npm i qvac-echo-plugin
    ```
  </Step>

  <Step>
    [In your `qvac.config.*`, add its `<package_name>/plugin` specifier alongside any built-in plugins](/configuration). For example:

    ```json title="qvac.config.json"
    {
      "plugins": [
        "@qvac/sdk/llamacpp-completion/plugin",
        "qvac-echo-plugin/plugin" // [!code highlight]
      ]
    }
    ```
  </Step>

  <Step>
    <Tabs>
      <Tab value="desktop" label="Node.js/Bare" default>
        When developing for desktop environment, use the SDK CLI to (re)bundle the SDK with all listed plugins:

        ```bash
        qvac bundle sdk
        ```
      </Tab>

      <Tab value="mobile" label="Expo">
        [When developing for mobile environment, use the Expo CLI to prebuild your project with all listed plugins:](/installation/#expo)

        ```bash
        npx expo prebuild
        ```
      </Tab>
    </Tabs>
  </Step>
</Steps>

<Callout type="success">
  **Tip:** when adding one or more custom plugins, you **must** also add **all** the built-in plugins you will need to use.
</Callout>

### Usage

Custom plugins are consumed as npm packages. Install the package, enable it in your `qvac.config.*` file like any built-in plugin, then import and use its API alongside the SDK’s regular API.

```ts
import { loadModel, unloadModel } from "@qvac/sdk";

// 1. Import the API you need from the custom plugin package:
import { echo, echoStream } from "qvac-echo-plugin";

// 2. Load the model(s) required by the custom plugin:
const modelId = await loadModel({
  modelSrc: "/path/to/echo-model.bin",
  modelType: "echo",
});

// 3. Call functions exposed by the custom plugin API:
const result = await echo({ modelId, message: "Hello, plugin system!" });
console.log(result);

for await (const char of echoStream({ modelId, message: "Streaming test!" })) {
  process.stdout.write(char);
}

await unloadModel({ modelId });
```

## Runtime registration on Bare

The `plugins` array in `qvac.config.*` is **bundle-time** configuration — it controls which addons get packed into your worker. That is separate from **runtime registration**, which determines the plugins live in the worker process when an SDK call runs.

On Node.js and Expo the SDK spawns a worker that auto-registers the full built-in set, so you never register manually. **Bare runs in-process with no spawned worker**, so nothing auto-registers — register the plugins you use before the first SDK call:

```js
import { plugins } from "@qvac/bare-sdk";
import { llmPlugin } from "@qvac/bare-sdk/llamacpp-completion/plugin";

const sdk = plugins([llmPlugin]); // or registerPlugin(llmPlugin) from "@qvac/bare-sdk/plugins"
```

Calls made before any plugin is registered raise `WorkerPluginsNotRegisteredError`.

<Callout type="info">
  For direct Bare usage we recommend [`@qvac/bare-sdk`](https://github.com/tetherto/qvac/tree/main/packages/bare-sdk) — the slim distribution built for explicit assembly. `@qvac/sdk` also runs on Bare; import the same plugins from `@qvac/sdk/<capability>/plugin` instead.
</Callout>

## Notes

* In `qvac.config.*`, if `plugins` is omitted or set to an empty array, the SDK bundles all built-in plugins. If `plugins` is set, it bundles only the listed plugins.
* [Each plugin maps to one QVAC addon](https://github.com/tetherto/qvac/tree/main/packages).
* [Write a custom plugin](/configuration/plugins/write-custom-plugin).
* **Programmatic bundling:** you can also prepare and validate the SDK bundle programmatically via API from a Node script. For example:

```ts
import { bundleSdk, verifyBundle } from "@qvac/sdk/commands";

await bundleSdk({
  projectRoot: process.cwd(),
  configPath: "./qvac.config.json",
  quiet: true,
});

const result = await verifyBundle({
  projectRoot: process.cwd(),
  addonsSource: "./qvac/worker.bundle.js",
  hosts: ["android-arm64", "ios-arm64"],
});
```
