Unified LLM service access layer — one API to access every provider (providers.md carries the count)
- Language Guides
- Quick Start
- Features
- Provider Factory Functions
- Feature Coverage
- Construction and base_url Support
- Design Documents
- License
Legacy (0.5 API). The language guides below still describe the 0.5 binding API, which V4 removed; each binding's
README.mdunder bindings/ describes the current API. V4 errors are the sharedErrorEnvelope{kind, code, message, error}with no retry hint: read a retry delay fromerror.responseHeaders(retry-after-ms, thenretry-after), as with the AI SDK. The rewrite is tracked in #277.
Each language has its own guide with that language's examples:
| Language | Guide | Coverage |
|---|---|---|
| Node.js | api/node.md | Full multimodal surface (native path) |
| Python | api/python.md | Full multimodal surface (native path) |
| Rust | api/rust.md | Full multimodal surface (core) |
| Go | api/go.md | Full multimodal surface (C ABI path, typed wrappers) |
| C/C++ | api/c.md | Full multimodal surface (C ABI) |
| Swift | api/swift.md | Full multimodal surface (C ABI path) |
| Kotlin | api/kotlin.md | Full multimodal surface (C ABI path) |
| Flutter/Dart | api/flutter.md | Full multimodal surface (C ABI path) |
| Java | api/java.md | Full multimodal surface (C ABI path) |
All bindings share the same API shape — only the syntax differs. Pick your language guide and follow its Quick Start:
Node.js · Python · Rust · Go · C/C++ · Swift · Kotlin · Flutter/Dart
OpenAI, Anthropic, Google, Bedrock, Vertex, Azure, Cohere, Mistral, xAI,
Anthropic-AWS — one constructor per provider: openai(apiKey, model, baseUrl?)
/ anthropic(apiKey, model, baseUrl?) (Node, Python), NewOpenAI(apiKey, model) (Go), Model.openai(apiKey, modelId) (Java, Kotlin, Flutter),
Aimux.openai(apiKey:modelId:) (Swift), create_openai(..) (Rust).
Multimodal, local-inference and search providers have their own constructors
too — full list: reference.md.
Rust uses aimux_providers::create_provider(name, PresetSettings) for vendor
packages and all 282 presets, then Provider::language_model(model_id).
default_providers() supplies a map for
aimux_core::provider_registry::create_provider_registry(providers, options),
whose model ids default to provider:model. provider_names() lists accepted
names; Provider::discovery() exposes runtime model listing.
The language bindings retain:
provider(name, api_key?, model_id, config?) // language bindings
name — vendor package or registry preset name as a string
api_key — optional; omitted/None reads the provider's env var
config — optional overrides (base_url / headers / organization / project / params)
`maxRetries` (a per-call option) and `bodyOverrides` (removed) are
rejected with InvalidArgument, not ignored
- Full list (name / env var / base URL): providers.md
- Custom endpoint: registry name +
base_urloverride, or the OpenAI constructor with a base URL
Non-streaming text generation; returns the complete result.
Examples: Node.js · Python · Rust · Go · Swift · Kotlin · Flutter · C ABI
| Parameter | Type | Description |
|---|---|---|
prompt |
string / Message[] |
Prompt or message array |
max_output_tokens |
number? |
Maximum number of generated tokens |
temperature |
number? |
Sampling temperature |
top_p |
number? |
Nucleus sampling |
stop_sequences |
string[]? |
Stop sequences |
tools |
Tool[]? |
List of available tools |
tool_choice |
ToolChoice? |
Tool selection strategy |
instructions |
string? |
System instructions |
reasoning |
ReasoningEffort? |
Reasoning effort |
max_retries |
number? |
Per-call retry override; 0 disables retries (None = 2) |
timeout |
TimeoutConfiguration? |
Per-call timeouts (total / step / first-chunk / chunk idle) — see Timeouts |
provider_options |
object? |
Per-provider options keyed by namespace; for OpenAI-compatible providers, unknown fields of the provider's own namespace go to the request body as given |
headers |
object? |
Extra HTTP headers |
Node.js additionally accepts an AbortSignal as the 4th argument of
generateText / streamText (see Request Cancellation).
The result is a structured object with these fields (the exact type declaration differs per language — see each language guide for its own declaration):
| Field | Description |
|---|---|
text |
Generated text of the final step (all Text parts concatenated) |
content |
The final step's content parts — see Structured content |
tool_calls |
The final step's tool calls, parsed and validated |
tool_results |
The final step's tool results (provider-executed tools) |
finish_reason |
Unified finish reason: stop, length, content-filter, tool-calls, error or other |
raw_finish_reason |
The provider's own finish reason |
usage |
Token usage (LanguageModelUsage) summed over the steps |
warnings |
Warnings of the final step |
steps |
Every model call of the generation (StepResult: content, finish reason, usage, performance, request, response with its messages, …) |
response_messages |
Assistant and tool messages to append to the next prompt |
output |
The value parsed by the output option (absent when not parsed) |
reasoning, reasoning_text, files, sources, request, response,
provider_metadata and total_usage are also present: the final step's
values (total_usage equals usage), deprecated in the AI SDK.
LanguageModelUsage is flat and camelCase, as in the AI SDK's JSON:
inputTokens, inputTokenDetails { noCacheTokens, cacheReadTokens, cacheWriteTokens }, outputTokens, outputTokenDetails { textTokens, reasoningTokens }, totalTokens and the provider's raw usage object.
An unknown count is absent (the AI SDK's undefined), never zero; the two
detail objects are always present. steps entries are camelCase as well
(stepNumber, callId, model { provider, modelId }, performance,
response { id, timestamp, modelId, headers?, body?, messages }), and each
toolResults entry carries type: "tool-result".
Note:
text,tool_callsand the other top-level fields are read from the final step'scontent. Tool calls are parsed and validated per the AI SDKparseToolCall/repairToolCallcontract: atool_callsentry carries the parsedinputplusprovider_metadata?,invalid?(truewhen tool lookup, input parse, or schema validation failed) anderror?(the serializedAiMuxError—NoSuchTool/InvalidToolInput/ToolCallRepair).
Type declarations are per-language. Every binding declares these types in its own syntax: TypeScript
interface/typein node.md, Python pydantic models in python.md, Ruststruct/enumin rust.md, Kotlindata classin kotlin.md, Dart classes in flutter.md, Swiftstructin swift.md, Gostructin go.md. The tables below describe the shared JSON shape that crosses the binding boundary — the field names and variant tags are identical in every language.
content is an array of tagged parts:
| Variant | Fields | Description |
|---|---|---|
Text |
text, provider_metadata? |
Generated text |
ToolCall |
tool_call_id, tool_name, input, provider_executed?, dynamic?, provider_metadata?, invalid?, error? |
Tool call requested by the model, input parsed (same shape as a tool_calls entry) |
Source |
source_type, id, url?, title?, … |
Reference/source |
Reasoning |
text, provider_metadata? |
Reasoning/thinking segment |
File / ReasoningFile |
data, media_type, provider_metadata? |
File generated by the model (or as part of reasoning) |
ToolResult |
tool_call_id, tool_name, result, is_error?, preliminary?, dynamic?, provider_metadata? |
Tool result executed by the provider |
ToolApprovalRequest |
approval_id, tool_call, … |
Approval request for a provider-executed tool call |
Custom |
kind, provider_metadata? |
Provider-specific content |
Returns generated content as a stream, output chunk by chunk.
Examples: Node.js · Python · Rust · Go · Swift · Kotlin · Flutter · C ABI
streamText yields the AI SDK's full stream (TextStreamPart): Start,
then for each step StartStep … FinishStep, then Finish — or Abort
when the call is aborted.
| Variant | Description |
|---|---|
Start |
Stream start |
StartStep |
Step start (carries request and the provider's warnings) |
TextStart / TextDelta / TextEnd |
Text segment lifecycle (TextDelta.text) |
ToolInputStart / ToolInputDelta / ToolInputEnd |
Tool calling input stream |
ToolCall |
Complete tool call (same shape as a tool_calls entry, incl. invalid? / error?) |
ToolResult |
Result of a provider-executed tool: tool_call_id, tool_name, input? (the call's input; absent for a deferred result), output, provider_executed, dynamic?, provider_metadata? |
ToolError |
tool_call_id, tool_name, input?, error, provider_executed?, dynamic?, provider_metadata?: a provider-executed tool's error result (provider_executed: true), or the error that follows an invalid client tool call (its error message, dynamic: true) |
ToolApprovalRequest |
Approval request for a provider-executed tool call |
ToolOutputDenied / ToolApprovalResponse |
Part of the AI SDK's set for denied tools and answered approvals; aimux's stream does not produce them yet |
ReasoningStart / ReasoningDelta / ReasoningEnd |
Reasoning segment lifecycle (ReasoningDelta.text) |
Source / File / ReasoningFile / Custom |
Content parts, as in content |
FinishStep |
Step end: response (id, timestamp, model_id, headers), usage, performance, finish_reason, raw_finish_reason, provider_metadata |
Finish |
Stream end: finish_reason, raw_finish_reason, total_usage |
Abort |
The call was aborted (reason?) |
Error |
Stream error |
Raw |
Provider raw chunk (for debugging, when include_raw_chunks is set) |
stream_text emits the call-layer TextStreamPart type. Provider-layer
StreamPart::ResponseMetadata events are consumed internally and do not
appear in this stream.
Calls can be cancelled mid-flight. Cancellation covers the whole request
lifecycle — connect, response headers, non-streaming body reads, and the
streaming body (including while waiting between chunks and during retry
backoff). An abort is reported as AiMuxError::Aborted (not retryable; a
pre-aborted signal fails fast without sending).
-
Node.js — pass an
AbortSignalas the last argument; the typed wrapper bridges it internally:import { openai, generateText, streamText } from '@arcships/aimux' const controller = new AbortController() const model = await openai('sk-...', 'gpt-4o') const result = await generateText(model, 'Explain Rust.', {}, controller.signal) const gen = streamText(model, 'Write a haiku.', {}, controller.signal) controller.abort() // cancels the stream promptly
Under the hood the wrapper constructs an
AbortBridge(also exported, for the raw napi surface and multimodal calls).AbortBridgeis one-shot: it shares the signal's cancellation state, so aborting once aborts every call that uses the same bridge, and reusing an aborted bridge fails fast. -
Rust — set
abort_signaldirectly on the options (a runtime handle, it never crosses the JSON boundary):let signal = aimux_core::AbortSignal::new(); let opts = GenerateTextOptions { abort_signal: Some(signal.clone()), ..Default::default() }; let task = tokio::spawn(generate_text(&model, "Explain Rust.", opts)); signal.abort(); // cancels the call
-
C ABI — cancellation rides a separate abort handle, not the JSON boundary:
aimux_abort_signal_new()/aimux_abort_signal_abort()/aimux_abort_signal_drop(), plus the*_with_abortentry points (aimux_stream_text_with_abort,aimux_stream_text_as_openai_with_abort) andaimux_transcription_session_new'sabort_handle. -
Go — idiomatic:
StreamTextContext/StreamTextAsOpenAIContexttake acontext.Contextand drive that abort handle for you (GenerateTexthas no context variant yet). -
Python / Swift / Kotlin / Java / Flutter — not yet exposed for generate/stream (tracked in RFC-0016 §7.3). Python's transcription session
close()aborts its driver, and the Swift / Kotlin / Java / FlutterstartStreamtakes anabortHandle— but only the C ABI exposes a way to create one (Go builds one internally from acontext.Contextand exposes none, so itsStartTranscriptionSessionWithAborthas no usable caller).
Per-call timeout limits, JSON-serializable in every binding:
| Field | Type | Description |
|---|---|---|
total_ms |
number? |
Overall deadline for the whole call — includes retries and, for streaming, the entire stream. 0 fails immediately |
first_chunk_ms |
number? |
Streaming only: time allowed from request start until the first chunk |
chunk_ms |
number? |
Streaming only: max idle time between chunks (sliding window, reset on every chunk) |
None/absent disables the corresponding limit. On expiry the call fails with
AiMuxError::Timeout (not retryable); streaming timeouts surface as a
TextStreamPart::Error item ("first chunk timeout" / "chunk idle timeout" /
"total timeout"). Unrepresentable values (e.g. u64::MAX ms on narrower
platforms) are rejected with AiMuxError::InvalidArgument instead of
panicking. When both abort and a deadline are in play, abort wins.
// Node.js — timeouts ride inside the options object
await generateText(model, 'Explain Rust.', {
timeout: { total_ms: 30_000, first_chunk_ms: 5_000, chunk_ms: 2_000 },
})// Rust
let opts = GenerateTextOptions {
timeout: Some(aimux_core::options::TimeoutConfiguration {
total_ms: Some(30_000),
first_chunk_ms: Some(5_000),
chunk_ms: Some(2_000),
}),
..Default::default()
};Vercel's
stepMs/toolMsare intentionally absent — they serve the multi-step tool loop (H4), which aimux does not implement (RFC-0016 §7.5).
Tool definitions are language-agnostic data descriptions (JSON Schema) that require no macros.
Examples: Node.js · Python · Rust
prompt accepts a message array to implement multi-turn conversation; roles support system / user / assistant / tool.
Examples: Node.js · Python · Rust
Converts text into a vector representation.
Examples: Node.js · Python · Rust · Go · C ABI
| Factory function | Provider | Representative model |
|---|---|---|
openaiEmbedding |
OpenAI | text-embedding-3-small/large |
cohereEmbedding |
Cohere | embed-english-v3.0 |
googleEmbedding |
gemini-embedding-001 |
Converts text into speech audio.
Examples: Node.js · Python · Rust · Go · C ABI
| Factory function | Provider | Representative model |
|---|---|---|
openaiSpeech |
OpenAI | tts-1, tts-1-hd |
Converts audio into text (non-streaming).
Examples: Node.js · Python · Rust · Go · C ABI
Examples: Node.js · Python · Rust · Go · C ABI
| Factory function | Provider | Representative model |
|---|---|---|
openaiImage |
OpenAI | dall-e-3 |
googleImage |
gemini-2.5-flash-image |
Most video models return a URL; the video operation downloads it, as the AI SDK's generateVideo does, and returns the bytes.
Examples: Node.js · Python · Rust · Go · C ABI
Reorders a document list by relevance.
Examples: Node.js · Python · Rust · Go · C ABI
Calls a search provider to obtain results.
Examples: Node.js · Python · Rust · Go · C ABI
⚠️ TheSearchModelclass is exported in Node.js / Python but there is no factory function in those bindings yet — use Rust, Go, or the C ABI.
Uploads a file to the provider and returns a file ID.
Examples: Node.js · Python · Rust · Go · C ABI
| Function | Provider | Example modelId |
|---|---|---|
openai(apiKey, modelId, baseUrl?) |
OpenAI | gpt-4o |
anthropic(apiKey, modelId, baseUrl?) |
Anthropic | claude-3-5-sonnet-20241022 |
deepseek(apiKey, modelId, baseUrl?) |
DeepSeek | deepseek-chat |
| Function | Provider | Example modelId |
|---|---|---|
openaiEmbedding(apiKey, modelId, baseUrl?) |
OpenAI | text-embedding-3-small |
cohereEmbedding(apiKey, modelId, baseUrl?) |
Cohere | embed-english-v3.0 |
googleEmbedding(apiKey, modelId, baseUrl?) |
gemini-embedding-001 |
| Function | Provider | Example modelId |
|---|---|---|
openaiSpeech(apiKey, modelId, baseUrl?) |
OpenAI | tts-1 |
| Function | Provider | Example modelId |
|---|---|---|
openaiTranscription(apiKey, modelId, baseUrl?) |
OpenAI | whisper-1 |
| Function | Provider | Example modelId |
|---|---|---|
openaiImage(apiKey, modelId, baseUrl?) |
OpenAI | dall-e-3 |
googleImage(apiKey, modelId, baseUrl?) |
gemini-2.5-flash-image |
| Function | Provider | Example modelId |
|---|---|---|
googleVideo(apiKey, modelId, baseUrl?) |
veo-3.0 |
| Function | Provider | Example modelId |
|---|---|---|
cohereReranking(apiKey, modelId, baseUrl?) |
Cohere | rerank-v3.0 |
| Function | Provider |
|---|---|
openaiFiles(apiKey, baseUrl?) |
OpenAI |
The
baseUrl?parameter of all factory functions is optional; by default each provider's official API address is used. When testing, pass a local mock server URL.
Per-language naming. The tables above use the Node.js (camelCase) names. Each binding has its own naming convention for the same factories: Python uses snake_case (
openai_embedding,google_video), Go usesNewXxxconstructors returning(T, error)(NewOpenAIEmbedding,NewGoogleVideo), and the C ABI usesaimux_<provider>_<feature>_new(aimux_openai_embedding_new). Swift/Kotlin/Flutter/Java expose the multimodal models as their own classes (EmbeddingModel.openai(...),SpeechModel.openai(...), …). See Feature Coverage for the full matrix and each language guide for examples.
Coverage verified against the current binding implementations (2026-08-01):
| Feature | Rust (core) | Node.js | Python | Swift | Kotlin | Flutter | Go | C/C++ | Java |
|---|---|---|---|---|---|---|---|---|---|
| Text generation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Streaming generation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Vector embedding | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Speech synthesis (TTS) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Speech to text (STT) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Image generation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Video generation | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Reranking | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Search | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| File upload | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
- ✅ — available. All bindings now expose the full multimodal surface.
- Node.js / Python (native path): full multimodal surface — every factory in the Provider Factory Functions section.
- Go (C ABI path): full multimodal surface with typed wrappers —
NewOpenAIEmbedding/NewCohereEmbedding/NewGoogleEmbedding,NewOpenAISpeech,NewOpenAITranscription,NewOpenAIImage/NewGoogleImage,NewOpenAIFiles,NewCohereReranking,NewGoogleVideo,NewTavilySearch, plusDeepSeek/NewDeepSeekand the typedGenerate/StreamAPI. All multimodal constructors supportWithBasevariants. - C/C++ (C ABI path): full multimodal surface via the C ABI function list.
- Swift / Kotlin / Flutter / Java (C ABI path): each now wraps all 8 multimodal model types alongside text generation and streaming. See each language guide for the API surface.
How this table was derived — every cell was checked against the binding's
own source (not inferred from another language). A feature counts as ✅ only
when the binding exposes a public factory and an invocable method for it;
| Binding | Evidence (source of truth) |
|---|---|
| Rust | aimux-core/src/ — one trait per feature: language_model.rs, embedding_model.rs, speech_model.rs, transcription_model.rs, image_model.rs, video_model.rs, reranking_model.rs, search_model.rs, files_model.rs; plus generate.rs (generate_text / stream_text) |
| Node.js | bindings/node/index.d.ts — 9 model classes plus a factory for every model type on the ./raw entry, tavilySearch (L499) included |
| Python | bindings/python/src/lib.rs — 8 multimodal classes registered via add_class, 11 multimodal factories via add_function, tavily_search included |
| Swift | bindings/swift/Sources/Aimux/Aimux.swift — Model (4 constructors) + generateText / streamText / streamTextAsync / generate; Multimodal.swift — 8 multimodal classes (EmbeddingModel, SpeechModel, TranscriptionModel, ImageModel, VideoModel, RerankingModel, SearchModel, Files) with factory constructors + methods + 19 Codable types |
| Kotlin | bindings/kotlin/src/main/kotlin/ai/arcships/aimux/Model.kt — JNA interface declares all 97 ABI functions; Multimodal.kt — 8 multimodal Closeable classes with factory methods; the DTOs come from the Java artifact's generated ai.arcships.aimux.types package |
| Flutter | bindings/flutter/lib/aimux.dart — Model (text); multimodal.dart — 8 multimodal classes with dart:ffi lookups for all 35 C ABI multimodal symbols (incl. the five RFC-0028 session entry points) |
| Go | bindings/go/multimodal.go — NewOpenAIEmbedding / NewOpenAISpeech / NewOpenAITranscription / NewOpenAIImage / NewGoogleVideo / NewCohereReranking / NewTavilySearch / NewOpenAIFiles + matching ParseXxxResult; embedding & image are OpenAI-only (no Cohere/Google constructors) |
| C/C++ | aimux-ffi/src/lib.rs — 113 exported extern "C" functions; full mapping in c.md |
The ❌ /
⚠️ cells are tracked as actionable work items in Binding API Gaps — each gap lists the required C ABI functions and a reference implementation.
| Binding | FFI method | base_url support | Construction example |
|---|---|---|---|
| Node.js | napi-rs (calls Rust directly) | ✅ 3rd parameter | await openai(key, model, 'http://localhost:3000') |
| Python | PyO3 (calls Rust directly) | ✅ 3rd parameter | openai(key, model, "http://localhost:3000") |
| Swift | C ABI (CAimuxFFI) | ✅ baseUrl: parameter |
try Model.openai(apiKey: key, modelId: model, baseUrl: url) |
| Kotlin | C ABI (JNA) | ✅ 3rd parameter | Model.openai(key, model, baseUrl) |
| Flutter/Dart | C ABI (dart:ffi) | ✅ baseUrl: named parameter |
Model.openai(key, model, baseUrl: url) |
| Go | C ABI (cgo static linking) | ✅ OpenAIWithBase |
aimux.OpenAIWithBase(key, model, url) |
| C/C++ | C ABI (direct linking) | ✅ _with_base function |
aimux_openai_new_with_base(key, model, url) |
The Node/Python bindings bypass the C ABI and call
aimux-providersdirectly; Swift/Kotlin/Flutter/Go/C go through theaimux-ffiC ABI. Go uses cgo to statically linklibaimux_ffi.a, producing a single binary (see RFC-0011 for details).
| Document | Content |
|---|---|
| RFC-0001 | Multi-language binding design |
| RFC-0003 | Cassette testing design |
| RFC-0008 | Multimodal binding design |
MIT