Skip to content

Implement hooks in another language

Implement selected Bub application hooks in Go, Rust, or another language through the experimental bub-extism plugin. Bub runs the Python application; Extism loads the WebAssembly modules that implement your hooks.

This guide connects two example modules. Check the plugin’s supported hooks and value conversions before choosing this integration for your application.

Make sure you have:

  • The Bub application, with bub --help working.
  • A Go toolchain if you plan to build the Go example.
  • A Rust toolchain with the wasm32-unknown-unknown target if you plan to build the Rust example.

The plugin documentation covers the supported hooks and module format:

Install bub-extism into the same environment where Bub runs:

bub install bub-extism@main

Verify that Bub picks it up:

bub hooks

You should see extism listed alongside builtin.

2. Implement one hook in Go and one in Rust

Section titled “2. Implement one hook in Go and one in Rust”

Use the two example modules shipped with bub-extism:

  • go-build-prompt implements the build_prompt hook
  • rust-run-model implements the run_model hook

Build the Go example:

cd bub-contrib/packages/bub-extism/examples/go-build-prompt
GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared -o go-build-prompt.wasm .

Build the Rust example:

cd bub-contrib/packages/bub-extism/examples/rust-run-model
cargo build --release --target wasm32-unknown-unknown

If you need the full build commands for either example, check the examples guide.

Create an extism.json in your project root that binds the prompt hook to the Go module and the model hook to the Rust module:

{
  "plugins": {
    "prompt": {
      "manifest": {
        "wasm": [
          {
            "path": "bub-contrib/packages/bub-extism/examples/go-build-prompt/go-build-prompt.wasm"
          }
        ]
      },
      "wasi": true,
      "hooks": {
        "build_prompt": "build_prompt"
      }
    },
    "model": {
      "manifest": {
        "wasm": [
          {
            "path": "bub-contrib/packages/bub-extism/examples/rust-run-model/target/wasm32-unknown-unknown/release/bub_extism_rust_run_model.wasm"
          }
        ]
      },
      "hooks": {
        "run_model": "run_model"
      }
    }
  }
}

The configuration connects the modules to the application:

  • manifest follows the standard Extism manifest format.
  • hooks maps Bub hook names to wasm exports.
  • Bub still owns dispatch and precedence.

Point Bub at the config file before running a turn:

export BUB_EXTISM_CONFIG_PATH=./extism.json

Now let Bub build a prompt first, then execute the model hook:

bub run "Say hello from a prompt built outside Python."

When everything is wired up correctly, Bub will:

  1. Call build_prompt inside the Go module
  2. Pass the returned prompt to run_model inside the Rust module
  3. Hand the final result back for this turn

The CLI prints outbound messages in [channel:chat_id] format, so a default bub run typically shows [cli:local] first, followed by the rendered output.

You can expect output similar to:

[cli:local]
[rust-run-model:cli:local] [go-build-prompt:cli:local] Say hello from a prompt built outside Python.

The Go module wraps the inbound prompt with a [go-build-prompt:<chat_id>] prefix; the Rust module then wraps the Go output with [rust-run-model:<chat_id>]. The leading [cli:local] line is the channel header emitted by the CLI adapter.

The Go module builds the prompt and the Rust module formats a response from it. These examples verify hook loading and data exchange. To use the builtin agent’s model calls and conversation recording, keep its run_model implementation.

Check that the plugin supports the hooks you need, and verify the arguments and return values passed between Python and WebAssembly.