# aimux
> **A unified LLM access layer written in Rust. One API for [327 provider names](docs/api/providers.md).**
> **0.6.0 is a breaking release.** Every binding now uses the shared operations
> ABI (ABI 1 / ops 1.0), and public JSON follows the AI SDK V4. See
> [Upgrading to 0.6](docs/upgrading-to-0.6.md) before moving a 0.5 integration.
[](https://github.com/arcships/aimux/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.rust-lang.org/)
[](docs/api/providers.md)
[](bindings/)
[](https://crates.io/crates/aimux-core)
[](https://www.npmjs.com/package/@arcships/aimux)
[](https://pypi.org/project/arcships-aimux/)
[](https://pkg.go.dev/github.com/arcships/aimux/bindings/go)
[](https://github.com/arcships/aimux/releases)
[](https://central.sonatype.com/artifact/ai.arcships/aimux-java)
aimux is a Rust implementation of a unified LLM provider access layer. It
collapses the HTTP APIs of every AI provider into a single
`dyn LanguageModel` interface that anything upstream can call.
Unlike **rig** or **langchain**, aimux does **not** build agent loops, RAG, or
orchestration — it focuses exclusively on unifying service access. That is the
difference: aimux is an access layer, those are orchestration layers.
---
For planned milestones and their current status, see the [roadmap](ROADMAP.md).
## Why aimux
- **327 provider names** — 282 registry-backed OpenAI-compatible presets
+ 45 vendor packages, covering text and other modalities. Local engines
such as Ollama/vLLM are presets. `provider_names()` lists the Rust names;
the generated catalogue lives in [docs/api/providers.md](docs/api/providers.md).
- **Unified, object-safe interface** — the `LanguageModel` trait supports
`Box` so providers are interchangeable without changing call sites.
- **Full multimodal** — text, streaming, tool calling, embeddings, image,
speech, transcription, video, reranking, files — plus **realtime
transcription sessions** (RFC-0028: push audio, pull transcript parts,
retryable timeouts).
- **Observability built in** (new in 0.3.0) — record every request/response
pair to JSONL and replay it offline (RFC-0023), group calls into sessions
(RFC-0024), and detect provider prompt-cache hits (RFC-0015).
- **Composite models** (new in 0.3.0) — `RouterModel` routes each call with
fallback (RFC-0021); `MoaModel` aggregates parallel reference models
mixture-of-agents style (RFC-0022). Both are plain `LanguageModel`s.
- **Config-driven provider registry** — `provider_registry.json` describes
each registry-backed OpenAI-compatible provider (base URL, env var, auth
mode, template parameters); the embedded JSON is parsed once into a runtime
descriptor table. Rust uses `create_provider(name, PresetSettings)` for
presets and vendor packages; bindings use `provider.create` +
`provider.model` and the shared operation names.
Packages follow the AI SDK shape: `XxxProviderSettings` + `create_xxx()` + a default `xxx()`.
- **Fast and small** — Rust core, release profile tuned for binary size
(`lto`, `codegen-units=1`, `panic="abort"`, `strip`, `opt-level="z"`).
- **8 language bindings** from one core: Node, Python, Swift, Kotlin, Flutter,
Go, Java, C — plus `aimux-web`, a browser console for model-call testing
and trace visualization (RFC-0029).
- **Hermetic tests** — 2,800+ cassettes replay real API responses; no network
or API keys required.
## Upgrading from 0.5
0.6.0 is a breaking release. The main changes:
- **Operations replace constructors.** Bindings use `provider.create` +
`provider.model` and the shared operation names; provider-specific
constructors and the 0.5 top-level helpers are removed.
- **Public JSON follows AI SDK V4.** Fields are camelCase, unions use the AI
SDK tags, and file/tool/usage/stream shapes are the AI SDK shapes.
- **Errors are the shared envelope.** Use `{kind, code, message, error}` and
the native `cause` chain; retry delays are read from `responseHeaders`, not
`retryMs`.
- **DTOs are generated.** Hand-written 0.5 wire types are removed; ABI 1 /
ops 1.0 and the manifest digest must match the native library.
- **Recording stays on schema 2.** Schema 3 and transport replay are a
follow-up.
Read the full [0.6 upgrade guide](docs/upgrading-to-0.6.md) before moving an
integration.
## Performance
Benchmarked against the official OpenAI SDKs on the same machine, same mock
server, same abstraction layer (HTTP + JSON, no orchestration). Full results
and methodology in [docs/PERF-RESULTS.md](docs/PERF-RESULTS.md).
| | aimux | OpenAI SDK | aimux faster |
|---|---|---|---|
| **Node.js** (single req) | 0.101 ms | 1.488 ms | **14.7×** |
| **Python** (single req) | 0.080 ms | 0.595 ms | **7.5×** |
### Sustained stress (2000 requests, 200 KB context, 50 KB response)
| | aimux rps | SDK rps | aimux P99 | SDK P99 | RSS growth |
|---|---|---|---|---|---|
| **Node.js** (32 cores) | 1512 | 563¹ | 1.92 ms | 3.96 ms | +23 MB vs +103 MB |
| **Python** | 1393 | 987 | 0.94 ms | 1.37 ms | **+0 MB** vs +8 MB |
¹ vs Vercel AI SDK (not apples-to-apples — AISDK adds Zod validation,
middleware, and telemetry per request).
### Why it's fast
- **Rust core** — `reqwest` connection pool, no GC, no runtime pauses.
- **Zero memory growth** — Python aimux RSS did not grow a single byte across
2000 requests; Node grew only 2 MB.
- **Stable tail latency** — no GC pauses means P99 stays flat even under CPU
contention; the JS SDK's P99 spikes to 12.87 ms on a single core.
- **C ABI overhead is low** — serialization is ~50% of overhead only on large
payloads; in real LLM requests (3–10 s) it is <0.1%.
## Architecture
```
aimux/
├── aimux-core # Core abstractions: LanguageModel / Provider / Message / StreamPart
├── aimux-providers # Provider implementations — registry-backed + typed (docs/api/providers.md)
├── aimux-ops # Shared operation dispatcher, manifest and generated DTOs
├── aimux-stream # SSE parsing
├── aimux-provider-utils # One-exchange HTTP helpers, response handlers, API-key loading, streamed tool-call tracking
├── aimux-ffi # C ABI (opaque handles + JSON results + owned aimux_error_t *) for non-native bindings
└── tools/ # aimux-cli (cache probe) · aimux-replay · aimux-web (console)
```
```
┌─ native path ──→ aimux-core + aimux-providers (direct Rust types + async)
bindings ──┤
└─ C ABI path ──→ aimux-ffi (handles + JSON results + owned errors)
```
## Installation
**Rust** (the core library):
```bash
cargo add aimux-core aimux-providers
```
| Crate | Description | crates.io |
|-------|-------------|-----------|
| `aimux-core` | Core abstractions: `LanguageModel` / `Provider` / `Message` / `StreamPart` | [crates.io](https://crates.io/crates/aimux-core) |
| `aimux-providers` | Provider implementations — [registry-backed + typed](docs/api/providers.md) | [crates.io](https://crates.io/crates/aimux-providers) |
| `aimux-ops` | Shared operation dispatcher, manifest and generated DTOs | [crates.io](https://crates.io/crates/aimux-ops) |
| `aimux-stream` | SSE parsing | [crates.io](https://crates.io/crates/aimux-stream) |
| `aimux-provider-utils` | One-exchange HTTP helpers, typed response handlers, streamed tool-call tracking | [crates.io](https://crates.io/crates/aimux-provider-utils) |
| `aimux-ffi` | C ABI for non-native bindings | [crates.io](https://crates.io/crates/aimux-ffi) |
**Node.js**:
```bash
npm install @arcships/aimux
```
```typescript
import { Client, LanguageModel } from '@arcships/aimux'
const client = new Client()
const provider = await client.provider({
package: 'openai',
settings: { apiKey: process.env.OPENAI_API_KEY },
})
const model = await provider.model('gpt-4o-mini', 'languageModel')
if (!(model instanceof LanguageModel)) throw new Error('Expected LanguageModel')
try {
const result = await model.generate('Explain Rust ownership in one sentence.')
console.log(result.text)
} finally {
await model.close()
await provider.close()
}
```
> The typed facade uses `Client` and the resource classes; the 0.5
> top-level `openai` and `generateText` helpers are removed. Need the raw
> JSON-string interface? Use `import { Runtime } from '@arcships/aimux/raw'`.
## Quick start
```rust
use aimux_core::prelude::*;
use aimux_providers::openai::openai;
#[tokio::main]
async fn main() -> Result<(), AiMuxError> {
// The default provider loads OPENAI_API_KEY when a request is made.
let model = openai().chat("gpt-4o");
let result = generate_text(
&model,
"Explain Rust ownership in one sentence.",
GenerateTextOptions::default(),
).await?;
println!("{}", result.text);
Ok(())
}
```
## Streaming
```rust
use futures::StreamExt;
let result = stream_text(
&model,
"Write a haiku about Rust.",
GenerateTextOptions::default(),
).await?;
let mut stream = result.stream;
while let Some(part) = stream.next().await {
match part? {
TextStreamPart::TextDelta { text, .. } => print!("{}", text),
TextStreamPart::Finish { .. } => println!("\n[done]"),
_ => {}
}
}
```
## Record & replay (new in 0.3.0)
Every request/response pair can be captured to JSONL and replayed offline —
regression tests, incident forensics, and CI without network or keys
([RFC-0023](rfc/0023-runtime-request-recording.md)):
```rust
use aimux_core::recording::{init_recording, JsonlRecorder};
use std::sync::Arc;
// One line turns on recording for the process (every binding has the same
// switch; secrets are redacted before hitting disk).
init_recording(Some(Arc::new(JsonlRecorder::new("./recordings"))));
let result = generate_text(&model, "Explain Rust ownership.", Default::default()).await?;
// → ./recordings/recordings.jsonl now holds the full request + response,
// replayable offline with the `aimux-replay` CLI.
```
Related: sessions group calls with `session_id` ([RFC-0024](rfc/0024-session-aggregation.md)),
and cache-hit tracing detects prompt-cache reuse from provider headers
([RFC-0015](rfc/0015-cache-trace-audit.md), surfaced by `aimux-cli`).
## Composite models: router & MoA (new in 0.3.0)
```rust
use aimux_core::composite::ChildModel;
use aimux_core::moa::{MoaConfig, MoaModel};
use aimux_core::router::{FallbackPolicy, RouterConfig, RouterModel, RuleRouter};
// Router: pick one model per call, fall back on failure (RFC-0021).
let provider = aimux_providers::openai::openai();
let children: Vec = vec![
Arc::new(provider.chat("primary-model-id")) as ChildModel,
Arc::new(provider.chat("secondary-model-id")) as ChildModel,
];
let routed = RouterModel::new(
children.clone(),
Box::new(RuleRouter),
FallbackPolicy::OnError,
RouterConfig::default(),
);
// MoA: fan out to references, aggregate their answers (RFC-0022).
let moa = MoaModel::new(
children,
Arc::new(provider.chat("primary-model-id")) as ChildModel,
MoaConfig::default(),
);
// Both are plain LanguageModel — every API and binding works unchanged.
let result = generate_text(&routed, "…", Default::default()).await?;
```
## Realtime transcription sessions (new in 0.3.0)
WebSocket-based streaming transcription with push-audio / next-part /
retryable timeouts, in every binding ([RFC-0028](rfc/0028-transcription-streaming.md)).
Python:
```python
from aimux import Client
client = Client()
with client.adopt(client.call("provider.create", {
"package": "openai",
"settings": {"apiKey": "your-api-key"},
})["handle"]) as provider:
with client.adopt(provider.call("provider.model", {
"method": "transcriptionModel", "modelId": "gpt-realtime-whisper",
})["handle"]) as model:
with client.adopt(model.call("transcription.start")) as operation:
operation.push(pcm_chunk) # push audio as it arrives
operation.call("operation.inputDone")
part = operation.call("operation.next", {"timeoutMs": 500})
print(part)
```
The same operation names are available from Node, Go, Java, Kotlin, Swift,
Flutter and C. See each language guide for the binding-specific wrapper.
## Switch providers
```rust
use aimux_core::provider_registry::create_provider_registry;
use aimux_providers::{create_provider, default_providers, provider_names, PresetSettings};
// Keys are loaded when a request is made.
let provider = create_provider("deepseek", PresetSettings::default())?;
let model = provider.language_model("deepseek-chat")?;
// Or assemble the built-in providers and resolve a provider:model id.
let registry = create_provider_registry(default_providers(), Default::default());
let model = registry.language_model("deepseek:deepseek-chat")?;
let names = provider_names();
// model usage is identical — it's all dyn LanguageModel
```
Rust by-name creation covers vendor packages and all 282 presets. Each vendor
also has `create_xxx(XxxProviderSettings {..})` and a default instance `xxx()`.
Bindings use `provider.create` and `provider.model` with the shared operation
names. Runtime model listing uses `Provider::discovery()` and then
`ProviderDiscovery::list_models()` — see
[docs/api/rust.md](docs/api/rust.md#providers).
## Provider coverage
The generated catalogue is [docs/api/providers.md](docs/api/providers.md).
The accepted Rust names come from `provider_names()`. The rough shape of
the roster:
- **Native protocol** — OpenAI, Anthropic, Google, Bedrock, Vertex, Azure, Cohere, Mistral, Groq, DeepSeek, xAI, Anthropic-AWS, Voyage
- **OpenAI-compatible (registry-backed)** — Fireworks, Together, Perplexity, OpenRouter, Ollama Cloud, Alibaba Tongyi, Zhipu, Baidu, Tencent, Moonshot, SiliconFlow…
- **Local and Vertex-hosted presets** — Ollama, vLLM, SGLang, Llama.cpp, LiteLLM Proxy, Vertex-hosted DeepSeek/Qwen/Llama…
- **Responses API for text** — xAI and Hugging Face (no chat-completions entry)
- **Speech / transcription** — ElevenLabs, Deepgram, AssemblyAI, AWS Polly, Cartesia, Hume, Gladia, RevAI, LMNT, Fal
- **Image / video** — Black Forest Labs, Replicate, Luma, Prodia, KlingAI, Recraft, Stability, RunwayML
- **Embeddings / rerank / search** — Voyage, Jina, Tavily, Exa, Firecrawl, Serper, SearXNG, You.com…
Full list with per-category counts: [docs/api/providers.md](docs/api/providers.md).
## Language bindings
aimux ships 8 bindings that share the same Rust core:
| Binding | Path | Tool | Get it | Native library |
|---------|------|------|--------|---------------|
| **Node.js** | native | napi-rs v3 | `npm install @arcships/aimux` — [npm](https://www.npmjs.com/package/@arcships/aimux) | bundled in the package, nothing to do |
| **Python** | native | PyO3 + maturin | `pip install arcships-aimux` — [PyPI](https://pypi.org/project/arcships-aimux/) | bundled in the wheel, nothing to do |
| **Go** | C ABI | cgo (static link) | `go get github.com/arcships/aimux/bindings/go` then `go generate` | auto-downloaded `.a` from [GitHub Releases](https://github.com/arcships/aimux/releases) |
| **Swift** | C ABI | SPM | Add `bindings/swift` as a local Swift package from a checkout (no root URL package) | `libaimux_ffi.dylib` — see [guide](docs/api/swift.md#install) |
| **Kotlin** | C ABI | JNA + coroutines | `ai.arcships:aimux-kotlin` — Maven Central · Java 8+ bytecode | `libaimux_ffi.so/.dylib` or `aimux_ffi.dll` on the JNA search path — see [guide](docs/api/kotlin.md#install) |
| **Java** | C ABI | JNA | `ai.arcships:aimux-java` — Maven Central · Java 8+ | same as Kotlin — see [guide](docs/api/java.md#install) |
| **Flutter** | C ABI | dart:ffi | `aimux` on pub.dev — Flutter plugin, native core embedded (publisher `arcships.ai`) | iOS/Android 开箱即用,桌面端开发/测试 — see [guide](docs/api/flutter.md#install) |
| **C / C++** | C ABI | direct link | `.so`/`.dylib`/`.dll` + `aimux.h` from [GitHub Releases](https://github.com/arcships/aimux/releases) | link against it — see [guide](docs/api/c.md#install) |
See [bindings/README.md](bindings/README.md) and the [API docs](docs/API.md).
## Testing
```bash
cargo test -p aimux-providers --tests
```
Tests run on cassette playback — no network and no keys. See
[rfc/0003-test-cassette.md](rfc/0003-test-cassette.md).
## Documentation
| Doc | Contents |
|-----|----------|
| [docs/API.md](docs/API.md) | **API overview** — shared reference + links to per-language guides |
| [docs/upgrading-to-0.6.md](docs/upgrading-to-0.6.md) | **0.6 migration guide** — breaking changes by language |
| [docs/api/reference.md](docs/api/reference.md) | **API reference** — public types & functions lookup |
| [docs/contributing/adding-a-provider.md](docs/contributing/adding-a-provider.md) | **Adding a provider** — the three-case checklist (registry row / new protocol / single modality) |
| [docs/api/providers.md](docs/api/providers.md) | **Provider list** — every provider with entry points and the canonical counts (generated) |
| [docs/api/](docs/api/) | **Per-language API guides** — Node.js, Python, Rust, Go, C/C++, Swift, Kotlin, Flutter |
| [docs/error-model.md](docs/error-model.md) | **错误模型** — 跨语言错误形态与兼容性约定 |
| [docs/PROJECT-OVERVIEW.md](docs/PROJECT-OVERVIEW.md) | Project overview, design decisions, benchmarks |
| [docs/PERF-RESULTS.md](docs/PERF-RESULTS.md) | Performance benchmark results |
| [docs/aimux-vs-aisdk-node.md](docs/aimux-vs-aisdk-node.md) | Node.js DX comparison vs Vercel AI SDK |
| [docs/README.md](docs/README.md) | Documentation index |
### Design docs (RFCs)
| RFC | Contents |
|-----|----------|
| [0001](rfc/0001-multilang-bindings.md) | Multi-language bindings (Node/Swift/Kotlin/Flutter/Python) |
| [0002](rfc/0002-provider-improvements.md) | Config descriptor & thin-wrapper improvements |
| [0003](rfc/0003-test-cassette.md) | Test cassette scheme |
| [0004](rfc/0004-provider-inventory.md) | Full provider inventory & implementation status |
| [0005](rfc/0005-protocol-conversion.md) | Protocol conversion & adaptation layer |
| [0006](rfc/0006-provider-development.md) | Provider minimum acceptance, core contract, tests |
| [0007](rfc/0007-search-model-trait.md) | Search model trait |
| [0008](rfc/0008-multimodal-bindings.md) | Multimodal bindings design |
| [0009](rfc/0009-request-resilience.md) | Request resilience (shared client / jitter / timeout) |
| [0010](rfc/0010-perf-benchmark-vs-aisdk.md) | Performance vs Vercel AI SDK benchmark |
| [0011](rfc/0011-golang-bindings.md) | Go bindings (cgo static link + push callback → channel) |
| [0012](rfc/0012-source-dedup.md) | Source dedup (product source −25%) |
| [0013](rfc/0013-java-bindings.md) | Java bindings (JNA + raw/typed two-layer API) |
| [0016](rfc/0016-align-with-aisdk.md) | Align with Vercel AI SDK (capability gaps) |
| [0017](rfc/0017-provider-config-dx.md) | Unified provider config & request body overrides (DX) |
| [0018](rfc/0018-codex-subscription.md) | Codex subscription channel provider (evaluation) |
| [0019](rfc/0019-session-affinity.md) | Session affinity lightweight support |
| [0014](rfc/0014-logging.md) | Logging (`AIMUX_LOG` controls) |
| [0015](rfc/0015-cache-trace-audit.md) | Cache-hit tracing & audit (TraceLayer / verdict evaluator) |
| [0020](rfc/0020-external-provider-config.md) | External OpenAI-compatible provider config (runtime registry overrides) |
| [0021](rfc/0021-composite-model-routing.md) | RouterModel — composite model routing with fallback |
| [0022](rfc/0022-moa-single-fanout.md) | MoaModel — single-fanout mixture-of-agents |
| [0023](rfc/0023-runtime-request-recording.md) | Request recording & replay (JSONL, ring, redaction) |
| [0024](rfc/0024-session-aggregation.md) | Session grouping & query APIs |
| [0025](rfc/0025-aimux-cli-cache-probe.md) | `aimux-cli` cache-probe client |
| [0026](rfc/0026-openai-compatible-output.md) | OpenAI-compatible output format |
| [0027](rfc/0027-model-catalogue-and-list-api.md) | Model catalogue & `list_models` |
| [0028](rfc/0028-transcription-streaming.md) | Realtime transcription streaming (WS sessions) |
| [0029](rfc/0029-web-console.md) | `aimux-web` browser console |
## Contributing
Contributions are welcome! Read [CONTRIBUTING.md](CONTRIBUTING.md) for the
development setup, testing workflow, provider/binding conventions, and the pull
request process. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
## License
[MIT](LICENSE)