Model or dataset
xybrid-ai/xybrid avatar
xybrid-ai/xybrid

Xybrid: on-device LLM, ASR and TTS for Flutter, Swift, Kotlin and Unity

Cross-platform on-device AI toolkit

450 stars51 forksRustApache-2.0

At a glance

What is it?
Xybrid is an Apache-2.0 Rust workspace that exposes llama.cpp and whisper.cpp through one model API across mobile, desktop, Unity and Python. The pitch is offline inference with no cloud dependency; the cost is a Bazel-plus-Cargo build and a very young 0.x SDK surface.
Who is it for?
Xybrid fits teams already shipping Flutter, Swift, Kotlin or Unity apps who need text, speech recognition or speech synthesis to run without a network round trip, and who can absorb a Bazel and Cargo toolchain plus a 0.x API that has moved from 0.6.0 to 0.8.0 in the README's own examples. It is the wrong pick if you need a hosted model, a stable frozen API, or a runtime you can debug without a Rust build.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Xybrid actually solves, and for whom

The README states the goal in one line: run LLMs, ASR and TTS natively in apps and games, private, offline, no cloud required. That sentence defines both the audience and the boundary. The audience is app and game developers who already have a Flutter, Swift, Kotlin, Unity, Rust or Python codebase and want inference inside the process rather than behind an HTTP call. The boundary is that Xybrid is not a model host, not a fine-tuning service, and not a replacement for a server-side inference stack.

What makes the project more than a thin wrapper is the single model API it puts in front of several runtimes. The README shows the same call shape in four languages: load a model by name, wrap input in an envelope, run it, get a result. In the Flutter example the model is kokoro-82m and the result is described as 24kHz WAV audio. The Kotlin and Swift examples use the identical model name and the identical comment. That consistency is the product. If you have shipped an app on three platforms, you know how much work it is to keep native inference code in sync across them; Xybrid's answer is to define the surface once and generate or bind it per platform.

The topics list confirms the intended scope: ios, kotlin, swift, unity3d, llamacpp, onnx-runtime, ollama, mobile-llm, voice-ai, edge-ai. The repository layout backs this up with a bindings directory, an examples directory split by platform (android, flutter, ios, unity), and a workspace of crates named xybrid-llama, xybrid-whisper, llama-cpp-sys and whisper-cpp-sys. This is a multi-language distribution project with a Rust core, not a single-library project.

How the Rust workspace is put together

The Cargo.toml lists the workspace members, and reading it is the fastest way to understand the architecture. There is a core crate, xybrid-core, plus xybrid-sdk, xybrid-ffi-facade, xybrid-bolt, and xybrid-cli. Two sys crates, llama-cpp-sys and whisper-cpp-sys, wrap the upstream C++ projects, and two safe wrappers, xybrid-llama and xybrid-whisper, sit on top of them. The Flutter binding has its own Rust crate at bindings/flutter/rust. Macros and xtask handle code generation and build tasks.

That layering tells you where the work happens. The sys crates are the boundary with C++; the xybrid-llama and xybrid-whisper crates are where Rust-side model handling lives; xybrid-ffi-facade is what the non-Rust SDKs call into. The presence of a facade crate named for FFI rather than for each language suggests one exported C surface that Swift, Kotlin, Flutter and Unity each wrap in their own idiom, which is consistent with the near-identical snippets in the README.

The version is declared once at [workspace.package] as 0.8.0, and the internal crates are referenced by path, so a release bumps one number. Note the gap between that and the README: the Flutter, Kotlin and Swift install snippets all say 0.6.0. The README is not tracking the workspace version. Before you pin anything, check crates.io, pub.dev, Maven Central or the release list rather than copying the snippet.

The most interesting entry in Cargo.toml is not a dependency but a build profile. Under [profile.dev.package."*"], opt-level is set to 3 and debug to false, with a comment explaining why. The comment states that without this, a flutter run debug build ships unoptimized inference kernels, and gives a measured figure from a Pixel 8: one 5 s Whisper-tiny window took minutes in debug versus about 3.5 s in release. It also notes the cost, a slower first cold build, and that debug is off because optimized dependency artifacts with full debuginfo are enormous, with a single Android cargokit build exceeding 45 GB. That is an unusually candid piece of build engineering to find in a manifest, and it is a warning about how you should test this project.

Installing Xybrid and running a first model

The README's Quick Start lists per-platform install paths and points to docs.xybrid.dev for the full installation guide. There is also a CLI badge linking to the GitHub releases page, and the repository contains install.sh and install.ps1 at the top level. The snippets below are copied from the README as they appear; treat the version numbers as examples, since the workspace declares 0.8.0 while the snippets say 0.6.0.

For a Flutter app, add the package to pubspec.yaml:

yaml
dependencies:
  xybrid_flutter: ^0.6.0

Then load a model and run it. The README's example loads kokoro-82m and passes a text envelope, and the comment says the result is 24kHz WAV audio:

dart
final model = await Xybrid.model('kokoro-82m').load();
final result = await model.run(XybridEnvelope.text('Hello world'));
// result → 24kHz WAV audio

For Kotlin, the dependency goes in build.gradle.kts and the call is asynchronous:

gradle
dependencies {
    implementation("ai.xybrid:xybrid-kotlin:0.6.0")
}
kotlin
val model = Xybrid.model("kokoro-82m").load()
val result = model.runAsync(Envelope.text("Hello world"))
// result → 24kHz WAV audio

For Swift, the package is added through Swift Package Manager in Package.swift, with the repository URL and a from: constraint:

swift
dependencies: [
    .package(url: "https://github.com/xybrid-ai/xybrid.git", from: "0.6.0")
]
swift
let model = try await Xybrid.model("kokoro-82m").load()
let result = try await model.runAsync(envelope: Envelope.text("Hello world"))
// result → 24kHz WAV audio

For Unity, the README recommends OpenUPM and gives a single command. It also notes that you can add https://package.openupm.com as a scoped registry for the scope ai.xybrid, and that a manual install adds the git subfolder as a package:

sh
openupm add ai.xybrid.sdk

The repeated model name across all four examples is worth noticing. kokoro-82m appears as a TTS model, and the README does not walk through an LLM or an ASR example in the snippets it shows, even though the project description names LLMs and ASR. To find those, you are directed to the per-platform SDK docs. The repository does carry examples for android, flutter, ios and unity, and an examples/README.md, which is where a working LLM or transcription sample would most plausibly live.

The debug-build trap that will waste your afternoon

The Cargo.toml comment about the dev profile is the single most useful operational fact in the repository, and it is buried in a manifest rather than in the README. The stated measurement is that a 5 second Whisper-tiny window took minutes in a debug build on a Pixel 8, against roughly 3.5 seconds in release. The workspace works around this by compiling all external dependencies at opt-level 3 even in dev builds, while keeping workspace crates at opt-level 0 for fast incremental compiles.

The trade-off is explicit: a slower first cold build, after which dependency artifacts are cached. The debug = false setting exists because optimized dependency artifacts with full debuginfo are huge, and the comment cites a single Android cargokit build exceeding 45 GB. The consequence for you is that backtraces through dependency code carry symbol names but not full debug information. If you are debugging a crash inside llama.cpp or whisper.cpp, that is a real constraint, and the project has chosen build size over debuggability of third-party code.

This matters beyond one profile setting. It means performance numbers gathered from a debug build are meaningless, and it means anyone evaluating Xybrid on a device should build in release or use the Bazel targets the justfile defines. It also suggests the maintainers hit this themselves on real hardware, which is a good sign for the mobile targets and a reminder that the desktop and server paths are not the ones being optimized.

The build system is not small, and that is the adoption cost

Xybrid ships two build paths. The justfile exposes plain Cargo recipes: build, build-release, test, check, lint, fmt, fmt-check, all operating on the workspace. It also exposes Bazel recipes that the comments say mirror CI, using bazelisk with a host config that adds --config=macos-metal on macOS. There are bazel-analyze and bazel-test recipes whose target lists are explicit, covering crates/xybrid-cli, xybrid-core, xybrid-ffi-facade, xybrid-llama, xybrid-sdk, integration-tests and xtask.

Having both is defensible, because a project that must produce Android, iOS, Unity and Flutter artifacts from a Rust core has genuinely hard cross-compilation problems, and Bazel with a hermetic toolchain is one way to solve them. The repository root supports this reading: .bazelrc, .bazelversion, MODULE.bazel, BUILD.bazel, a bazel directory, and patches named hermetic_llvm.patch, rules_android_ndk.patch, rules_foreign_cc.patch, rules_rs.patch and rules_rust.patch. A vendor directory and a .cargo directory are also present. That is a lot of build machinery, and patches against upstream rules are maintenance debt that someone has to track.

The practical question is which path you need. If you are consuming xybrid from crates.io as a Rust dependency, the Cargo path is the one that matters and the Bazel graph is the maintainers' problem. If you are building the Flutter, Android or Unity artifacts yourself, or contributing, you are in the Bazel path and its target lists. The justfile makes that distinction visible, which is more than many multi-language projects do, but the presence of a vendor directory and five rule patches should temper any expectation that a from-source build is a quick clone-and-go.

Where Xybrid is the wrong tool

The clearest limitation is the one the README states as a feature: no cloud required. That is a benefit for privacy and offline use and a hard ceiling everywhere else. Model size is bounded by the device. There is no server-side batching across users, no shared KV cache, no way to swap in a frontier model that will not fit on a phone, and no centralized place to update model weights without shipping an app update. If your requirement is the best possible answer quality and latency is negotiable, a hosted API will beat an on-device runtime and Xybrid is the wrong layer.

The second limitation is API stability. The workspace declares 0.8.0 and the README's install snippets say 0.6.0. The release list shows v0.7.0 on 2026-08-28 and v0.8.0 on 2026-09-08, roughly eleven days apart. That cadence is good for a young project and bad for anyone who wants a frozen surface. A 0.x version number is an explicit statement that breaking changes are expected, and the README's own drift from the workspace version is evidence that documentation lags releases. Pin exact versions and read the CHANGELOG before upgrading.

The third is platform risk. The README labels the Web SDK as a preview, so the web target is not at parity with Flutter, Swift, Kotlin, Unity and Rust. If your product is a browser app, the preview badge is the answer to whether you should build on it today. And if your team has no Rust experience, you are adopting a C++ and Rust toolchain whether or not you write any Rust, because the sys crates and the FFI facade are what your Swift, Kotlin or Flutter code ultimately calls.

How Xybrid differs from llama.cpp and ONNX Runtime directly

The honest alternative is to skip Xybrid and call the underlying runtimes yourself. The repository's own dependency list names them: llama-cpp-sys wraps llama.cpp, whisper-cpp-sys wraps whisper.cpp, and the topics list includes onnx-runtime. If you only need LLM inference on one platform, llama.cpp already ships bindings and a CLI, and you would avoid the facade, the macro crate, the Bazel graph and the rule patches entirely.

The difference in approach is what Xybrid adds on top. llama.cpp gives you one engine and one language binding per platform; Xybrid gives you one model API across Flutter, Swift, Kotlin, Unity, Rust and Python, and a single envelope abstraction for text input and audio output. That abstraction is the value, and it is only worth paying for if you actually have more than one client platform. A single-platform iOS app gains little from a cross-platform facade and inherits its build complexity.

ONNX Runtime is a different comparison. It is a general inference engine with its own model format and a broad operator set, and it is not tied to llama.cpp or whisper.cpp. Xybrid's crates are named for those two projects specifically, so the model families you can run are shaped by what those runtimes support. If your models are already ONNX graphs, running them through ONNX Runtime directly is the shorter path; Xybrid is aimed at the LLM, ASR and TTS case the README names, not at arbitrary graph execution. The topics list mentions both onnx-runtime and ollama, but the README's examples and the crate names point at llama.cpp and whisper.cpp as the engines actually in play.

Licence, governance and what an upgrade costs

Xybrid is Apache-2.0, declared in both the LICENSE file and [workspace.package] in Cargo.toml. Apache-2.0 is a permissive licence with an explicit patent grant and a requirement to preserve notices, which matters here because the project links against llama.cpp and whisper.cpp through sys crates. Those upstream projects carry their own licences, and the repository includes a NOTICE file, which is the conventional place to record third-party attribution. If you redistribute a binary built from this workspace, the notice obligations of the whole dependency tree are yours to satisfy, not just the Apache-2.0 terms of Xybrid itself. That is a statement about what the repository contains, not legal advice.

The governance files are present and unusually complete for a project this young: GOVERNANCE.md, MAINTAINERS.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, a CLA.md and a CLAUDE.md, plus AGENTS.md and an agents directory. The OpenSSF Scorecard and Best Practices badges in the README point at public scorecards, which is a verifiable signal rather than a claim. None of that tells you how many people are paying attention, and the README's star and visitor badges are not evidence of anything about quality.

Upgrade cost is the practical question, and two data points are visible. The release cadence is fast: v0.7.0 on 2026-08-28, v0.8.0 on 2026-09-08. And the README's install snippets are already behind the workspace version, which means documentation updates trail releases. Budget for reading CHANGELOG.md and RELEASES.md at each bump rather than assuming the snippets you copied will still compile. For a Rust consumer the version is centralized at [workspace.package], so internal crates move together; for a Flutter, Kotlin or Swift consumer you are pinning a published artifact whose version is independent of that number and must be checked on pub.dev, Maven Central or the release page.

Editorial conclusion

Xybrid fits teams already shipping Flutter, Swift, Kotlin or Unity apps who need text, speech recognition or speech synthesis to run without a network round trip, and who can absorb a Bazel and Cargo toolchain plus a 0.x API that has moved from 0.6.0 to 0.8.0 in the README's own examples. It is the wrong pick if you need a hosted model, a stable frozen API, or a runtime you can debug without a Rust build. Verify three things first: that the crate and package versions you pin actually match the release you intend to ship, that a release-mode build is what you measure on your target device, and that the model you want is one the SDK documents. The last push was on 2026-09-15 and v0.8.0 was tagged on 2026-09-08, so the project is moving; treat the version numbers in the README's snippets as illustrative rather than current.

Frequently asked questions

What platforms does Xybrid support?

The README lists Flutter, Swift, Kotlin, Unity and Rust, with a Python binding in the repository at bindings/python and a Web SDK labelled as a preview. Each platform badge links to its own setup page on docs.xybrid.dev. The Web target is explicitly a preview rather than a supported release.

Does Xybrid require an internet connection or a cloud service?

The README describes the project as private, offline, no cloud required, and the model API loads a model by name and runs it locally. The examples return audio directly from the run call. There is no hosted inference service described in the README.

Why is Xybrid so slow when I run it in a debug build?

The workspace Cargo.toml comments explain that a flutter run debug build would otherwise ship unoptimized inference kernels, and cite a Pixel 8 measurement where one 5 second Whisper-tiny window took minutes in debug against about 3.5 seconds in release. The workspace sets opt-level 3 for all external dependencies in the dev profile to work around this, at the cost of a slower first cold build.

Which model should I load first with Xybrid?

Every quick-start snippet in the README uses kokoro-82m and describes the output as 24kHz WAV audio, so that is the example the documentation gives for a first run. It is a text-to-speech model, and the README does not show an LLM or ASR snippet in the Quick Start section. The per-platform SDK docs and the examples directory are where other model types would be documented.

What version of Xybrid should I pin?

The workspace declares 0.8.0 in Cargo.toml, while the Flutter, Kotlin and Swift install snippets in the README all reference 0.6.0. The release list shows v0.7.0 on 2026-08-28 and v0.8.0 on 2026-09-08. Check the published package on pub.dev, Maven Central, crates.io or the releases page rather than copying the snippet version.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. xybrid-ai/xybrid on GitHub
Community notes

Community notes