
A high-performance inference engine for AI models. It allows you to deploy AI directly in your app with zero latency, full data privacy, and no inference costs. Key features:
- Simple, high-level API
- Unified model configurations, making it easy to add support for new models
- Traceable computations to ensure correctness against the source-of-truth implementation
- Utilizes unified memory on Apple devices
- Broad model support
Rust
Add the dependency:
[dependencies]
uzu = { git = "https://github.com/trymirai/uzu", branch = "main", package = "uzu" }Run the code below:
use uzu::{
engine::{Engine, EngineConfig},
types::session::chat::{ChatConfig, ChatMessage, ChatReplyConfig},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine_config = EngineConfig::default();
let engine = Engine::new(engine_config).await?;
let model = engine.model("Qwen/Qwen3-0.6B".to_string()).await?.ok_or("Model not found")?;
let downloader = engine.download(&model).await?;
while let Some(update) = downloader.next().await {
println!("Download progress: {}", update.progress());
}
let session = engine.chat(model, ChatConfig::default()).await?;
let messages = vec![
ChatMessage::system().with_text("You are a helpful assistant".to_string()),
ChatMessage::user().with_text("Tell me a short, funny story about a robot".to_string()),
];
let replies = session.reply(messages, ChatReplyConfig::default()).await?;
if let Some(reply) = replies.last() {
println!("Reasoning: {}", reply.message.reasoning().unwrap_or_default());
println!("Text: {}", reply.message.text().unwrap_or_default());
}
Ok(())
}Python
Add the dependency:
uv add uzu==0.5.14Run the code below:
import asyncio
from uzu import ChatConfig, ChatMessage, ChatReplyConfig, Engine, EngineConfig
async def main() -> None:
engine_config = EngineConfig.create()
engine = await Engine.create(engine_config)
model = await engine.model("Qwen/Qwen3-0.6B")
if model is None:
return
async for update in (await engine.download(model)).iterator():
print(f"Download progress: {update.progress}")
session = await engine.chat(model, ChatConfig.create())
messages = [
ChatMessage.system().with_text("You are a helpful assistant"),
ChatMessage.user().with_text("Tell me a short, funny story about a robot"),
]
replies = await session.reply(messages, ChatReplyConfig.create())
if not replies:
return
message = replies[-1].message
print(f"Reasoning: {message.reasoning}")
print(f"Text: {message.text}")
if __name__ == "__main__":
asyncio.run(main())Swift
Add the dependency:
dependencies: [
.package(url: "https://github.com/trymirai/uzu.git", from: "0.5.14")
]Run the code below:
import Uzu
public func runQuickStart() async throws {
let engineConfig = EngineConfig.create()
let engine = try await Engine.create(config: engineConfig)
guard let model = try await engine.model(identifier: "Qwen/Qwen3-0.6B") else {
return
}
for try await update in try await engine.download(model: model).iterator() {
print("Download progress: \(update.progress())")
}
let session = try await engine.chat(model: model, config: .create())
let messages = [
ChatMessage.system().withText(text: "You are a helpful assistant"),
ChatMessage.user().withText(text: "Tell me a short, funny story about a robot")
]
let reply = try await session.reply(input: messages, config: .create())
guard let message = reply.last?.message else {
return
}
print("Reasoning: \(message.reasoning() ?? "empty")")
print("Text: \(message.text() ?? "empty")")
}TypeScript
Add the dependency:
pnpm add @trymirai/uzu@0.5.14Run the code below:
import { ChatConfig, ChatMessage, ChatReplyConfig, Engine, EngineConfig } from '@trymirai/uzu';
async function main() {
let engineConfig = EngineConfig.create();
let engine = await Engine.create(engineConfig);
let model = await engine.model('Qwen/Qwen3-0.6B');
if (!model) {
throw new Error('Model not found');
}
for await (const update of await engine.download(model)) {
console.log('Download progress:', update.progress);
}
let session = await engine.chat(model, ChatConfig.create());
let messages = [
ChatMessage.system().withText('You are a helpful assistant'),
ChatMessage.user().withText('Tell me a short, funny story about a robot')
];
let reply = await session.reply(messages, ChatReplyConfig.create());
let message = reply[0]?.message;
if (message) {
console.log('Reasoning: ', message.reasoning);
console.log('Text: ', message.text);
}
}
main().catch((error) => {
console.error(error);
});Everything from model downloading to inference configuration is handled automatically. Refer to the documentation for details on how to customize each step of the process.
You can run any example via cargo tools example <rust | python | swift | typescript> <chat | chat-cloud | chat-structured-output | classification | quick-start>:
In this example, we will download a model and get a reply to a specific list of messages:
Rust
use uzu::{
engine::{Engine, EngineConfig},
session::chat::ChatSessionStreamChunk,
types::session::chat::{ChatConfig, ChatMessage, ChatReplyConfig},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine_config = EngineConfig::default();
let engine = Engine::new(engine_config).await?;
let model = engine.model("Qwen/Qwen3-0.6B".to_string()).await?.ok_or("Model not found")?;
let downloader = engine.download(&model).await?;
while let Some(update) = downloader.next().await {
println!("Download progress: {}", update.progress());
}
let messages = vec![
ChatMessage::system().with_text("You are a helpful assistant".to_string()),
ChatMessage::user().with_text("Tell me a short, funny story about a robot".to_string()),
];
let session = engine.chat(model, ChatConfig::default()).await?;
let stream = session.reply_with_stream(messages, ChatReplyConfig::default()).await;
let mut last_message: Option<ChatMessage> = None;
while let Some(chunk) = stream.next().await {
match chunk {
ChatSessionStreamChunk::Replies {
replies,
} => {
if let Some(reply) = replies.first() {
last_message = Some(reply.message.clone());
println!("Generated tokens: {}", reply.stats.tokens_count_output.unwrap_or_default());
}
},
ChatSessionStreamChunk::Error {
error,
} => {
println!("Error: {error}");
},
}
}
if let Some(message) = last_message {
println!("Reasoning: {}", message.reasoning().unwrap_or_default());
println!("Text: {}", message.text().unwrap_or_default());
}
Ok(())
}Python
import asyncio
from uzu import (
ChatConfig,
ChatMessage,
ChatReplyConfig,
ChatSessionStreamChunk,
Engine,
EngineConfig,
)
async def main() -> None:
engine_config = EngineConfig.create()
engine = await Engine.create(engine_config)
model = await engine.model("Qwen/Qwen3-0.6B")
if model is None:
raise RuntimeError("Model not found")
async for update in (await engine.download(model)).iterator():
print(f"Download progress: {update.progress}")
messages = [
ChatMessage.system().with_text("You are a helpful assistant"),
ChatMessage.user().with_text("Tell me a short, funny story about a robot"),
]
session = await engine.chat(model, ChatConfig.create())
stream = await session.reply_with_stream(messages, ChatReplyConfig.create())
message: ChatMessage | None = None
async for chunk in stream.iterator():
if isinstance(chunk, ChatSessionStreamChunk.Replies):
replies = chunk.replies
if replies:
reply = replies[0]
message = reply.message
print(f"Generated tokens: {reply.stats.tokens_count_output}")
elif isinstance(chunk, ChatSessionStreamChunk.Error):
print(f"Error: {chunk.error}")
if message is not None:
print(f"Reasoning: {message.reasoning}")
print(f"Text: {message.text}")
if __name__ == "__main__":
asyncio.run(main())Swift
import Uzu
public func runChat() async throws {
let engineConfig = EngineConfig.create()
let engine = try await Engine.create(config: engineConfig)
guard let model = try await engine.model(identifier: "Qwen/Qwen3-0.6B") else {
return
}
for try await update in try await engine.download(model: model).iterator() {
print("Download progress: \(update.progress())")
}
let messages = [
ChatMessage.system().withText(text: "You are a helpful assistant"),
ChatMessage.user().withText(text: "Tell me a short, funny story about a robot")
]
let session = try await engine.chat(model: model, config: .create())
let stream = await session.replyWithStream(input: messages, config: .create())
var message: ChatMessage? = nil
for try await update in stream.iterator() {
switch update {
case .replies(let replies):
let reply = replies.last
message = reply?.message
print("Generated tokens: \(reply?.stats.tokensCountOutput ?? 0)")
case .error(let error):
print("Error: \(error)")
}
}
print("Reasoning: \(message?.reasoning() ?? "empty")")
print("Text: \(message?.text() ?? "empty")")
}TypeScript
import { ChatConfig, ChatMessage, ChatReplyConfig, ChatSessionStreamChunkError, ChatSessionStreamChunkReplies, Engine, EngineConfig } from '@trymirai/uzu';
async function main() {
let engineConfig = EngineConfig.create();
let engine = await Engine.create(engineConfig);
let model = await engine.model('Qwen/Qwen3-0.6B');
if (!model) {
throw new Error('Model not found');
}
for await (const update of await engine.download(model)) {
console.log('Download progress:', update.progress);
}
let messages = [
ChatMessage.system().withText('You are a helpful assistant'),
ChatMessage.user().withText('Tell me a short, funny story about a robot')
];
let session = await engine.chat(model, ChatConfig.create());
let stream = await session.replyWithStream(messages, ChatReplyConfig.create());
let message: ChatMessage | undefined;
for await (const chunk of stream) {
if (chunk instanceof ChatSessionStreamChunkReplies) {
message = chunk.replies[0]?.message;
console.log('Generated tokens: ', chunk.replies[0]?.stats.tokensCountOutput);
} else if (chunk instanceof ChatSessionStreamChunkError) {
console.error('Error: ', chunk.error);
}
}
console.log('Reasoning: ', message?.reasoning);
console.log('Text: ', message?.text);
}
main().catch((error) => {
console.error(error);
});
Once loaded, the same ChatSession can be reused for multiple requests until you drop it. Each model may consume a significant amount of RAM, so it's important to keep only one session loaded at a time. For iOS apps, we recommend adding the Increased Memory Capability entitlement to ensure your app can allocate the required memory.
In this example, we will get a reply to a specific list of messages from a cloud model:
Rust
use uzu::{
engine::{Engine, EngineConfig},
types::{
basic::ReasoningEffort,
session::chat::{ChatConfig, ChatMessage, ChatReplyConfig},
},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine_config = EngineConfig::default().with_openai_api_key("OPENAI_API_KEY".to_string());
let engine = Engine::new(engine_config).await?;
let model = engine.model("gpt-5".to_string()).await?.ok_or("Model not found")?;
let messages = vec![
ChatMessage::system().with_reasoning_effort(ReasoningEffort::Low),
ChatMessage::user().with_text("How LLMs work".to_string()),
];
let session = engine.chat(model, ChatConfig::default()).await?;
let replies = session.reply(messages, ChatReplyConfig::default()).await?;
if let Some(reply) = replies.first() {
println!("Reasoning: {}", reply.message.reasoning().unwrap_or_default());
println!("Text: {}", reply.message.text().unwrap_or_default());
}
Ok(())
}Python
import asyncio
from uzu import ChatConfig, ChatMessage, ChatReplyConfig, Engine, EngineConfig, ReasoningEffort
async def main() -> None:
engine_config = EngineConfig.create().with_openai_api_key("OPENAI_API_KEY")
engine = await Engine.create(engine_config)
model = await engine.model("gpt-5")
if model is None:
raise RuntimeError("Model not found")
messages = [
ChatMessage.system().with_reasoning_effort(ReasoningEffort.Low),
ChatMessage.user().with_text("How LLMs work"),
]
session = await engine.chat(model, ChatConfig.create())
replies = await session.reply(messages, ChatReplyConfig.create())
if replies:
message = replies[0].message
print(f"Reasoning: {message.reasoning}")
print(f"Text: {message.text}")
if __name__ == "__main__":
asyncio.run(main())Swift
import Uzu
public func runChatCloud() async throws {
let engineConfig = EngineConfig.create().withOpenaiApiKey(openaiApiKey: "OPENAI_API_KEY")
let engine = try await Engine.create(config: engineConfig)
guard let model = try await engine.model(identifier: "Qwen/Qwen3-0.6B") else {
return
}
let messages = [
ChatMessage.system().withReasoningEffort(reasoningEffort: .low),
ChatMessage.user().withText(text: "How LLMs work")
]
let session = try await engine.chat(model: model, config: .create())
let reply = try await session.reply(input: messages, config: .create())
guard let message = reply.last?.message else {
return
}
print("Reasoning: \(message.reasoning() ?? "empty")")
print("Text: \(message.text() ?? "empty")")
}TypeScript
import { ChatConfig, ChatMessage, ChatReplyConfig, Engine, EngineConfig, ReasoningEffort } from '@trymirai/uzu';
async function main() {
let engineConfig = EngineConfig.create().withOpenaiApiKey('OPENAI_API_KEY');
let engine = await Engine.create(engineConfig);
let model = await engine.model('gpt-5');
if (!model) {
throw new Error('Model not found');
}
let messages = [
ChatMessage.system().withReasoningEffort("Low" as ReasoningEffort),
ChatMessage.user().withText('How LLMs work')
];
let session = await engine.chat(model, ChatConfig.create());
let reply = await session.reply(messages, ChatReplyConfig.create());
let message = reply[0]?.message;
if (message) {
console.log('Reasoning: ', message.reasoning);
console.log('Text: ', message.text);
}
}
main().catch((error) => {
console.error(error);
});Sometimes you want the generated output to be valid JSON with predefined fields. You can use Grammar to manually specify a JSON schema for the response you want to receive:
Rust
use schemars::{JsonSchema, schema_for};
use serde::{Deserialize, Serialize};
use uzu::{
engine::{Engine, EngineConfig},
types::{
basic::{Grammar, ReasoningEffort},
session::chat::{ChatConfig, ChatMessage, ChatReplyConfig},
},
};
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct Country {
name: String,
capital: String,
}
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
struct CountryList {
countries: Vec<Country>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine_config = EngineConfig::default();
let engine = Engine::new(engine_config).await?;
let model = engine.model("Qwen/Qwen3-0.6B".to_string()).await?.ok_or("Model not found")?;
let downloader = engine.download(&model).await?;
while let Some(update) = downloader.next().await {
println!("Download progress: {}", update.progress());
}
let schema_string = serde_json::to_string(&schema_for!(CountryList))?;
let messages = vec![
ChatMessage::system().with_reasoning_effort(ReasoningEffort::Disabled),
ChatMessage::user().with_text(
"Give me a JSON object containing a list of 3 countries, where each country has name and capital fields"
.to_string(),
),
];
let session = engine.chat(model, ChatConfig::default()).await?;
let chat_reply_config = ChatReplyConfig::default().with_grammar(Some(Grammar::JsonSchema {
schema: schema_string,
}));
let replies = session.reply(messages, chat_reply_config).await?;
if let Some(reply) = replies.first()
&& let Some(text) = reply.message.text()
{
let parsed: CountryList = serde_json::from_str(&text)?;
println!("{parsed:#?}");
}
Ok(())
}Python
import asyncio
import json
from pydantic import BaseModel
from uzu import (
ChatConfig,
ChatMessage,
ChatReplyConfig,
Engine,
EngineConfig,
Grammar,
ReasoningEffort,
)
class Country(BaseModel):
name: str
capital: str
class CountryList(BaseModel):
countries: list[Country]
def structured_response(response: str | None, model_type: type[BaseModel]) -> BaseModel | None:
if not response:
return None
return model_type.model_validate_json(response)
async def main() -> None:
engine_config = EngineConfig.create()
engine = await Engine.create(engine_config)
model = await engine.model("Qwen/Qwen3-0.6B")
if model is None:
raise RuntimeError("Model not found")
async for update in (await engine.download(model)).iterator():
print(f"Download progress: {update.progress}")
schema_string = json.dumps(CountryList.model_json_schema())
messages = [
ChatMessage.system().with_reasoning_effort(ReasoningEffort.Disabled),
ChatMessage.user().with_text(
"Give me a JSON object containing a list of 3 countries, where each country has name and capital fields"
),
]
session = await engine.chat(model, ChatConfig.create())
replies = await session.reply(
messages,
ChatReplyConfig.create().with_grammar(Grammar.JsonSchema(schema_string)),
)
if replies:
countries = structured_response(replies[0].message.text, CountryList)
print(countries)
if __name__ == "__main__":
asyncio.run(main())Swift
import FoundationModels
import Uzu
@Generable()
struct Country: Codable {
let name: String
let capital: String
}
public func runChatStructuredOutput() async throws {
let engineConfig = EngineConfig.create()
let engine = try await Engine.create(config: engineConfig)
guard let model = try await engine.model(identifier: "Qwen/Qwen3-0.6B") else {
return
}
for try await update in try await engine.download(model: model).iterator() {
print("Download progress: \(update.progress())")
}
let messages = [
ChatMessage.system().withReasoningEffort(reasoningEffort: .disabled),
ChatMessage.user().withText(text: "Give me a JSON object containing a list of 3 countries, where each country has name and capital fields")
]
let session = try await engine.chat(model: model, config: .create())
let reply = try await session.reply(input: messages, config: .create().withGrammar(grammar: .fromType([Country].self)))
guard let message = reply.last?.message else {
return
}
guard let countries: [Country] = message.textDecoded() else {
return
}
print(countries)
}TypeScript
import { ChatConfig, ChatMessage, ChatReplyConfig, Engine, EngineConfig, GrammarJsonSchema, ReasoningEffort } from '@trymirai/uzu';
import * as z from "zod";
const CountryType = z.object({
name: z.string(),
capital: z.string(),
});
const CountryListType = z.array(CountryType);
function structuredResponse<T extends z.ZodType>(response: string | null | undefined, type: T): z.infer<T> | undefined {
if (!response) {
return undefined;
}
const data = JSON.parse(response);
const result = type.parse(data);
return result;
}
async function main() {
let engineConfig = EngineConfig.create();
let engine = await Engine.create(engineConfig);
let model = await engine.model('Qwen/Qwen3-0.6B');
if (!model) {
throw new Error('Model not found');
}
for await (const update of await engine.download(model)) {
console.log('Download progress:', update.progress);
}
let schema = z.toJSONSchema(CountryListType);
let schemaString = JSON.stringify(schema);
let messages = [
ChatMessage.system().withReasoningEffort("Disabled" as ReasoningEffort),
ChatMessage.user().withText('Give me a JSON object containing a list of 3 countries, where each country has name and capital fields')
];
let session = await engine.chat(model, ChatConfig.create());
let reply = await session.reply(messages, ChatReplyConfig.create().withGrammar(new GrammarJsonSchema(schemaString)));
let message = reply[0]?.message;
let countries = structuredResponse(message?.text, CountryListType);
console.log(countries);
}
main().catch((error) => {
console.error(error);
});In this example, we will use a classification model to determine whether the user's input is safe from a moderation perspective:
Rust
use uzu::{
engine::{Engine, EngineConfig},
types::session::classification::ClassificationMessage,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let engine_config = EngineConfig::default();
let engine = Engine::new(engine_config).await?;
let model = engine.model("trymirai/chat-moderation-router".to_string()).await?.ok_or("Model not found")?;
let downloader = engine.download(&model).await?;
while let Some(update) = downloader.next().await {
println!("Download progress: {}", update.progress());
}
let messages = vec![ClassificationMessage::user("Hi".to_string())];
let session = engine.classification(model).await?;
let output = session.classify(messages).await?;
println!("Output: {:?}", output.probabilities.values);
Ok(())
}Python
import asyncio
from uzu import ClassificationMessage, Engine, EngineConfig
async def main() -> None:
engine_config = EngineConfig.create()
engine = await Engine.create(engine_config)
model = await engine.model("trymirai/chat-moderation-router")
if model is None:
raise RuntimeError("Model not found")
async for update in (await engine.download(model)).iterator():
print(f"Download progress: {update.progress}")
messages = [ClassificationMessage.user("Hi")]
session = await engine.classification(model)
output = await session.classify(messages)
print(f"Output: {output.probabilities.values}")
if __name__ == "__main__":
asyncio.run(main())Swift
import Uzu
public func runClassification() async throws {
let engine = try await Engine.create(config: .create())
guard let model = try await engine.model(identifier: "trymirai/chat-moderation-router") else {
return
}
for try await update in try await engine.download(model: model).iterator() {
print("Download progress: \(update.progress())")
}
let messages = [
ClassificationMessage.user(content: "Hi")
]
let session = try await engine.classification(model: model)
let output = try await session.classify(input: messages)
print("Output: \(output.probabilities.values)")
}TypeScript
import { ClassificationMessage, Engine, EngineConfig } from '@trymirai/uzu';
async function main() {
let engineConfig = EngineConfig.create();
let engine = await Engine.create(engineConfig);
let model = await engine.model('trymirai/chat-moderation-router');
if (!model) {
throw new Error('Model not found');
}
for await (const update of await engine.download(model)) {
console.log('Download progress:', update.progress);
}
let messages = [
ClassificationMessage.user('Hi')
];
let session = await engine.classification(model);
let output = await session.classify(messages);
console.log('Output: ', output.probabilities.values);
}
main().catch((error) => {
console.error(error);
});uzu is a native Rust crate with bindings available for:
It supports:
- Backends:
metalcpu
- Targets:
aarch64-apple-darwinaarch64-apple-iosaarch64-apple-ios-simaarch64-pc-windows-msvc(in progress)aarch64-unknown-linux-gnu(in progress)wasm32-wasip1-threads(in progress)x86_64-apple-darwinx86_64-pc-windows-msvc(in progress)x86_64-unknown-linux-gnu(in progress)
For initial setup we recommend running
cargo tools setup, which installs all necessary dependencies (rustup, uv, pnpm, Rust targets, Metal toolchain, ...) if not already present.
To unify cross-language development we introduce
cargo tools:
- Install language specific dependencies:
cargo tools install typescript - Build:
cargo tools build rust --targets apple - Test:
cargo tools test python - Run example:
cargo tools example swift chat
uzu uses its own model format. You can download a test model:
./scripts/download_test_model.shOr download any supported model that has already been converted:
cd ./tools/
uv run downloader list # show the list of supported models
uv run downloader download {REPO} # download a specific modelModels downloaded for development are stored at ./workspace/models/0.5.14/.
You can also export a model yourself with lalamo:
git clone https://github.com/trymirai/lalamo.git
cd lalamo
uv run lalamo list-models
uv run lalamo convert meta-llama/Llama-3.2-1B-InstructYou can run uzu in CLI mode:
cargo run --release -p cliThis launches an interactive app where you can browse, download, and interact with models.
You can also preselect a model with --model, passing its identifier or repository id:
cargo run --release -p cli -- --model trymirai/Qwen3.5-4B-MIf the model is not downloaded yet, the CLI starts downloading it automatically.
To run benchmarks:
cargo run --release -p cli -- bench ./workspace/models/0.5.14/{MODEL_NAME} ./workspace/models/0.5.14/{MODEL_NAME}/benchmark_task.json ./workspace/models/0.5.14/{MODEL_NAME}/benchmark_result.jsonbenchmark_task.json is automatically generated after the model is downloaded via ./tools/.
You can also run uzu as an OpenAI-compatible HTTP server:
cargo run --release -p cli -- server --model trymirai/Qwen3.5-4B-MThe model is loaded on startup (and downloaded first if needed). By default the server listens on 127.0.0.1:8000; override the address with --host and --port:
cargo run --release -p cli -- server --model trymirai/Qwen3.5-4B-M --host 0.0.0.0 --port 8080It exposes the following endpoints, available both at the root and under /v1:
POST /v1/chat/completions— chat completions, with streaming when"stream": true. Honorstemperature,top_p,top_k, andmax_tokens.GET /v1/models— lists the loaded model.
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "trymirai/Qwen3.5-4B-M",
"messages": [{"role": "user", "content": "Hello!"}],
"stream": true
}'If you experience any problems, please contact us via Discord or email.
This project is licensed under the MIT License. See the LICENSE file for details.