Skip to content

Latest commit

 

History

History
182 lines (142 loc) · 6.33 KB

File metadata and controls

182 lines (142 loc) · 6.33 KB

aimux · Flutter/Dart API

Unified LLM service access layer — one API to access 325 AI providers

Flutter/Dart wraps the Rust core through the aimux-ffi C ABI (via dart:ffi).

Install

On pub.dev (publisher: arcships.ai):

flutter pub add aimux

The package is a Flutter plugin: the Rust core ships inside it — libaimux_ffi.so per ABI (Android) and aimux_ffi.xcframework (iOS) are embedded at publish time, so no extra download or build step is needed. iOS integrates via SwiftPM (default on Flutter 3.44+, ios/aimux/Package.swift) or CocoaPods fallback (ios/aimux.podspec).

While developing against the repo, depend on the path:

dependencies:
  aimux:
    path: bindings/flutter

Desktop (Linux/macOS/Windows) is supported for development and tests: the library is resolved from the platform library path — build it once with cargo build -p aimux-ffi --release and point LD_LIBRARY_PATH (DYLD_LIBRARY_PATH on macOS) at target/release.

The binding loads the platform library at runtime (libaimux_ffi.so / libaimux_ffi.dylib / aimux_ffi.dll) — ship it with your app or place it where the loader can find it.

Quick Start

final model = Model.openai('sk-...', 'gpt-4o', baseUrl: 'http://localhost:3000');
final result = model.generateText('What is Rust?');
model.close();

Providers

All 250 registry-backed OpenAI-compatible providers are reachable by name; ProviderName holds the constants:

Scope: provider(name) covers only the 250 registry OpenAI-compatible providers; Anthropic/Google/multimodal/local → typed factories (Model.anthropic(apiKey, modelId)); custom endpoints → base-URL variant. Full list: providers.md.

// 推荐:ProviderName.groq 常量(补全 + 防拼写错误)
final model = Model.provider(ProviderName.groq, 'llama-3.3-70b');
final result = model.generateText('Hello');
model.close();

// 字符串形式同样可用 + 可选 config JSON ({"base_url": "..."}):
final model2 = Model.provider('groq', 'llama-3.3-70b', apiKey: 'sk-...');
model2.close();

Unknown names throw NoSuchProviderError (payload: the provider id); valid names come from the generated ProviderName constants.

Errors

Engine and binding failures throw an AimuxException subclass hierarchy (idiomatic Dart — is / on type checks, not stringly code switches):

Exception (implements)
 └── AimuxException
      ├── UnknownError
      ├── JSONParseError / InvalidResponseDataError
      ├── ToolError
      ├── InvalidArgumentError / InvalidPromptError
      ├── TokenExpiredError         // status 401
      ├── UnsupportedFunctionalityError
      ├── NoSuchModelError / NoSuchProviderError
      ├── APICallError              // every HTTP-shaped failure; branch on status
      ├── AimuxTimeoutError
      ├── RequestAbortedError
      └── OtherError

Every instance has message, code (AimuxErrorCode constants matching C AimuxErrorCode), status (HTTP or -1), and retryMs (hint or -1; 0 = retry now). Built from the C AimuxError out-param via AimuxException.fromC — constructors return uint64_t (0 = failure), payload calls return char* (NULL = failure), streams return int32_t (0 = failure). No JSON error-envelope sniffing on the main path.

import 'package:aimux/aimux.dart'; // exports errors.dart

try {
  final result = model.generateText('hi');
} on APICallError catch (e) {
  if (e.status == 429) {
    // rate limited; e.retryMs is the hint
  } else if (e.status == 401) {
    // auth failure
  }
} on AimuxException catch (e) {
  // any engine / binding failure
}

Local closed-handle checks still throw StateError (not AimuxException). Stream terminal failures surface via Stream.addError(AimuxException) — there is no on_error callback; provider mid-stream StreamPart::Error is data on on_part.

Text Generation

final model = Model.openai('sk-...', 'gpt-4o');
final result = model.generateText('What is Rust?');
model.close();

Parameters, return value, and the raw.content variants are documented in the API overview.

Streaming Generation

// streaming
final model = Model.openai('sk-...', 'gpt-4o');
final stream = model.streamText('Write a haiku');
await for (final part in stream) {
  if (part.containsKey('TextDelta')) print(part['TextDelta']['delta']);
}
model.close();

Stream part variants are documented in the API overview.

TypedModel

The raw Model speaks JSON maps. TypedModel wraps it with typed objects:

final model = TypedModel(Model.openai('sk-...', 'gpt-4o'));
final result = model.generateText('What is Rust?');
print(result.text);  // typed GenerateTextResult

final stream = model.streamText('Write a haiku');
await for (final part in stream) {
  if (part is StreamPartTextDelta) print(part.delta);  // typed StreamPart variants
}
model.close();
API Signature Description
TypedModel TypedModel(Model raw) — wrap a raw Model
generateText GenerateTextResult generateText(String prompt, [GenerateTextOptions? options]) String prompt
generateTextMessages GenerateTextResult generateTextMessages(List<ModelMessage> messages, [GenerateTextOptions? options]) Multi-turn typed messages
streamText Stream<StreamPart> streamText(Object prompt, [GenerateTextOptions? options]) Yields typed StreamParts
close void close() Release the native handle

Types

bindings/flutter/lib/types.dart declares the typed model surface (with toJson / fromJson on each): Role, FinishReasonUnified, ReasoningEffort, TokenUsage, Usage, FinishReason, ToolCall, FunctionTool, Tool, ToolChoice, ResponseMetadata, GenerateContent (sealed), GenerateResult, GenerateTextResult, GenerateTextOptions, ModelMessage, StreamPart (sealed), FileBytes, FileData, ContentPart.

Coverage

Text generation and streaming are supported. Multimodal features (embedding, TTS, STT, image, video, rerank, search, files) are reachable only through the raw C ABI until the wrappers are extended — see the coverage matrix.