Expand description
§foundation-models
Safe, idiomatic Rust bindings for Apple’s FoundationModels framework — the on-device large language model that ships with Apple Intelligence on macOS 26+.
§Features
- Sessions and multi-turn chat — create, restore, inspect, and persist
LanguageModelSessions - Streaming — text deltas and structured-generation snapshots
- Async API — executor-agnostic
Futurewrappers forrespond(to:),respond(to:generating:), and adapter lifecycle (seeasync_apimodule) - Tool calling — register Rust callbacks as
FoundationModelsTools - Structured generation — JSON-schema validation, dynamic schemas, string-choice schemas, array guides, and Rust
Generabletraits - Structured content helpers — typed
GenerationId, string-backedDecimal,GeneratedContentKind, and generated-content builders with optional IDs - System model configuration — availability, use cases, guardrails, locales, and adapter handles
- Transcript support — typed transcript inspection plus raw JSON round-tripping
- Response / tool definitions —
ResponseFormat::generating, inferredTool::generable, and transcriptToolDefinitionhelpers - Typed error metadata —
FMErroraccessors for recovery suggestions, refusal helpers, tool-call details, and generation/schema/adapter-error contexts - Feedback attachments — full
LanguageModelFeedbackissue/sentiment support
§Requirements
- macOS 26.0 or newer (build host and runtime)
- Xcode 26 SDK
- Apple Intelligence enabled in System Settings
- Apple Silicon
§Installation
[dependencies]
foundation-models = { version = "0.9.0", features = ["macos_26_0"] }§Async API
Enable the async feature to get executor-agnostic Future wrappers that
work with any async runtime (Tokio, async-std, smol, pollster, …):
[dependencies]
foundation-models = { version = "0.9.0", features = ["macos_26_0", "async"] }use foundation_models::{LanguageModelSession, SystemLanguageModel};
use foundation_models::async_api::AsyncSession;
if !SystemLanguageModel::is_available() { return Ok(()); }
pollster::block_on(async {
let session = LanguageModelSession::new();
let reply = AsyncSession::new(&session).respond("Name three Norse gods.")?.await?;
println!("{}", reply.content);
Ok::<(), Box<dyn std::error::Error>>(())
})| Type | Apple API |
|---|---|
AsyncSession::respond | LanguageModelSession.respond(to:) |
AsyncSession::respond_generating | LanguageModelSession.respond(to:generating:) |
AsyncAdapter::from_name | SystemLanguageModel.Adapter init(name:) |
AsyncAdapter::compatibility | Adapter.compatibleAdapterIdentifiers(name:) |
Tier 2 note:
LanguageModelSession.streamResponse(to:)is anAsyncSequence(multi-fire stream). It is deferred to Tier 2. UseLanguageModelSession::streamfor synchronous streaming today.
§Quick start
use foundation_models::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
if !SystemLanguageModel::is_available() {
eprintln!("Unavailable: {:?}", SystemLanguageModel::availability());
return Ok(());
}
let session = LanguageModelSession::with_instructions(
"Answer in a single concise sentence.",
);
let reply = session.respond("Why is the sky blue?")?;
println!("{reply}");
Ok(())
}§Tool calling
use foundation_models::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct EchoArgs {
message: String,
}
let schema = GenerationSchema::from_dynamic(
DynamicGenerationSchema::object("EchoArgs").with_property(
"message",
DynamicGenerationProperty::new(DynamicGenerationSchema::string()),
),
[],
)?;
let tool = Tool::json("echo", "Echo the provided message.", schema, |args: EchoArgs| {
Ok(args.message)
});
let session = LanguageModelSession::builder()
.instructions("Use tools when explicitly asked.")?
.tool(tool)
.build()?;
let reply = session.respond_prompt(
"Use the echo tool exactly once with the message 'hello from Rust'.",
)?;
println!("{reply}");§Structured generation
use foundation_models::prelude::*;
let schema = GenerationSchema::from_dynamic(
DynamicGenerationSchema::object("Movie")
.with_property(
"title",
DynamicGenerationProperty::new(DynamicGenerationSchema::string()),
)
.with_property(
"year",
DynamicGenerationProperty::new(DynamicGenerationSchema::integer()),
),
[],
)?;
let session = LanguageModelSession::new();
let response = session.respond_generated(
"Return JSON for one classic science-fiction movie.",
&schema,
true,
)?;
println!("{}", response.json_string()?);§Coverage-oriented helpers
use foundation_models::prelude::*;
let schema = GenerationSchema::from_dynamic(
DynamicGenerationSchema::array_of(DynamicGenerationSchema::string()).with_guides([
GenerationGuide::minimum_count(1),
GenerationGuide::maximum_count(3),
GenerationGuide::element(GenerationGuide::string_pattern("^[a-z]+$")),
]),
[],
)?;
let response_format = ResponseFormat::generating::<GeneratedContent>()?;
let tool = Tool::generable("echo", "Echo structured content", |args: GeneratedContent| {
Ok(args.json_string()?)
})?;
println!("{}", schema.json_schema());
println!("{}", response_format.name());
println!("{}", tool.definition().name);§Smoke example
cargo run --example 06_smoke --features macos_26_0
cargo run --example 07_schema_surface --features macos_26_0§Notes
- Swift-only compile-time macros such as
@Generableand@Guideare exposed as Rust runtime traits/builders (Generable,GenerationGuide,DynamicGenerationSchema). SystemLanguageModel.Adapter::isCompatible(_ assetPack:)is not wrapped because it depends onBackgroundAssets.AssetPack, which this crate does not expose.GenerationIDnow round-trips asGenerationIdviaGeneratedContent::generation_id_handle();GeneratedContent::generation_id()remains as a best-effort string helper.- Typed generation/schema/adapter error metadata plus refusal helpers are available through
FMError::{generation_error_context, adapter_asset_error_context, schema_error_context, recovery_suggestion, failure_reason, refusal, tool_call_error}. - Xcode 26.2’s
FoundationModels.swiftinterfacedoes not expose standalonePromptTag,Conversation,ToolCallingMode,SystemPrompt,Examples,LanguageModelInputContent,LanguageModelOutputContent, orStreamingsymbols; seeCOVERAGE.mdfor the audited matrix.
§License
Licensed under either of Apache-2.0 or MIT at your option.
§API Documentation
Safe, idiomatic Rust bindings for Apple’s FoundationModels framework — the on-device large language model that ships with Apple Intelligence.
Generate text, hold multi-turn conversations, and stream tokens from the system language model on macOS 26.0+.
§Quick start
use foundation_models::{LanguageModelSession, SystemLanguageModel};
if !SystemLanguageModel::is_available() {
eprintln!("Model unavailable: {:?}", SystemLanguageModel::availability());
return Ok(());
}
let session = LanguageModelSession::new();
let reply = session.respond("Name three Norse gods.")?;
println!("{reply}");§Streaming
use foundation_models::{LanguageModelSession, StreamEvent};
use std::io::Write;
let session = LanguageModelSession::new();
session.stream("Tell me a haiku about Rust.", |event| match event {
StreamEvent::Chunk(s) => {
print!("{s}");
std::io::stdout().flush().ok();
}
StreamEvent::Done => println!(),
StreamEvent::Error(e) => eprintln!("\nstream error: {e}"),
_ => {}
})?;Re-exports§
pub use content::Decimal;pub use content::FromGeneratedContent;pub use content::GeneratedContent;pub use content::GeneratedContentKind;pub use content::GenerationId;pub use content::ToGeneratedContent;pub use error::AdapterAssetErrorContext;pub use error::FMError;pub use error::GenerationErrorContext;pub use error::Refusal;pub use error::SchemaErrorContext;pub use error::ToolCallError;pub use generation::GenerationOptions;pub use generation::SamplingMode;pub use model::Adapter;pub use model::Availability;pub use model::ConfiguredSystemLanguageModel;pub use model::Guardrails;pub use model::SystemLanguageModel;pub use model::UseCase;pub use prompt::Instructions;pub use prompt::Prompt;pub use prompt::ResponseFormat;pub use prompt::Segment;pub use prompt::StructuredSegment;pub use prompt::TextSegment;pub use prompt::ToInstructions;pub use prompt::ToPrompt;pub use prompt::ToolDefinition;pub use schema::DynamicGenerationProperty;pub use schema::DynamicGenerationSchema;pub use schema::Generable;pub use schema::GenerationGuide;pub use schema::GenerationSchema;pub use session::FeedbackAttachmentRequest;pub use session::FeedbackIssue;pub use session::FeedbackIssueCategory;pub use session::FeedbackSentiment;pub use session::LanguageModelSession;pub use session::SessionBuilder;pub use session::SessionResponse;pub use session::StreamEvent;pub use session::StructuredStreamEvent;pub use session::StructuredStreamSnapshot;pub use tool::Tool;pub use tool::ToolOutput;pub use tool::ToolSpec;pub use transcript::Entry as TranscriptEntry;pub use transcript::ToolCall;pub use transcript::ToolCalls;pub use transcript::ToolOutput as TranscriptToolOutput;pub use transcript::Transcript;pub use transcript::TranscriptInstructions;pub use transcript::TranscriptPrompt;pub use transcript::TranscriptResponse;
Modules§
- async_
api async - Executor-agnostic async API for
FoundationModels(Tier 1). - content
GeneratedContent— structured model output represented as JSON.- error
- Errors produced by the
FoundationModelsbridge. - ffi
- Raw FFI declarations matching the Swift
@_cdeclexports inswift-bridge/Sources/FoundationModelsBridge. - generation
- Knobs that control how the model produces text.
- model
SystemLanguageModel— entry point for querying device capability and building configured model handles.- prelude
- Common imports for users of this crate.
- prompt
- Prompt and instructions builders.
- schema
- JSON-schema and dynamic schema builders for structured generation.
- session
LanguageModelSession— a stateful conversation with the on-device model.- tool
- Tool calling support.
- transcript
- Transcript inspection and restoration.