On this page · 13 sections
- What is actually in the package
- The problem: transcripts grow, context windows do not
- Skills: procedural context loaded only when asked for
- ChatCompletionsLanguageModel: one Swift API, any server
- Where the utilities sit in the WWDC 2026 model line-up
- The error surface you now have to handle
- Measure the compaction, do not assume it
- The Python SDK closes the evaluation loop
- India-specific considerations
- An adoption checklist
- FAQ
- How eCorpIT can help
- References
Summary. Apple published apple/foundation-models-utilities under the Apache-2.0 licence, delivering the open-source promise it made at WWDC 2026 on 9 June 2026. The package adds four things the base Foundation Models framework leaves to you: a Skills type that loads task instructions only when the model asks for them, three history-compaction modifiers (rollingWindow, droppingCompletedToolCalls, summarizeHistory), a ChatCompletionsLanguageModel that points a LanguageModelSession at any chat-completions server, and support for Linux distributions such as Ubuntu alongside the Apple platforms. The framework itself now spans 4 model paths in one Swift API: the on-device SystemLanguageModel, PrivateCloudComputeLanguageModel, CoreAILanguageModel for your own weights, and MLXLanguageModel pulling from Hugging Face. Apple made Private Cloud Compute inference free for developers under 2 million first-time App Store downloads, which reframes the build-versus-buy maths against hosted rates such as Claude Opus 5 at $5 and $25 per million input and output tokens as of 24 July 2026. The catch nobody puts on a slide: every one of these compaction tricks can invalidate the key-value cache and add latency, so the pattern you pick is a performance decision, not a tidiness decision.
Session code samples in this article are quoted from Apple's own WWDC 2026 sessions and the package README, not reconstructed.
What is actually in the package
The FoundationModelsUtilities package is a single Swift library target with an Apache-2.0 licence and a Swift Package Manager manifest that resolves from: "1.0.0". Apple describes its scope in the README as "extra utilities for working with LLMs via the Foundation Models framework, such as custom skills, context management helpers, and a chat completions client that connects to a hosted model of your choice."
Three details in that repository matter more than the headline. First, the supported platforms line names "Apple platforms and select Linux distributions like Ubuntu", which puts Apple's session-and-transcript abstractions on a server for the first time. Second, the repository ships a skills/ directory whose stated purpose is to "teach your favorite coding agent how to use this package", so the package documents itself for agentic editors. Third, issue reporting goes to the Apple Developer Forums rather than GitHub issues, which tells you how Apple intends to run it.
Adding it to an existing app is a one-line dependency:
let package = Package(
name: "YourApp",
dependencies: [
.package(url: "https://github.com/apple/foundation-models-utilities", from: "1.0.0")
],
targets: [
.target(
name: "YourApp",
dependencies: [
.product(name: "FoundationModelsUtilities", package: "foundation-models-utilities")
]
)
]
)
The base framework it extends targets .macOS(.v27), .iOS(.v27), .visionOS(.v27) and .watchOS(.v27) in Apple's own model-package template, so this is an iOS 27 era API. Teams still shipping to iOS 26 devices need the fallback path described in our on-device versus cloud AI build guide.
The problem: transcripts grow, context windows do not
A LanguageModelSession keeps a Transcript of entries: instructions, prompts, tool calls, tool outputs and responses. Apple's session 242, "Build agentic app experiences with the Foundation Models framework", spells out what happens next. Transcripts grow past the context limit, stale tool output distracts the model, and private content accumulates in a structure you may not want to keep.
The framework's answer is historyTransform, a stateless per-request transform over the history window. The utilities package turns the common transforms into ready-made modifiers so you stop hand-rolling index arithmetic:
import FoundationModelsUtilities
struct CraftProfile: LanguageModelSession.DynamicProfile {
var orchestrator: CraftOrchestrator
var body: some DynamicProfile {
switch orchestrator.mode {
case .reviewing:
Profile { CraftCoach() }
// Keep the most recent 10 entries
// after dropping finished tool calls
.rollingWindow(size: .entries(10))
.droppingCompletedToolCalls()
}
}
}
The README shows all three composed, with summarisation gated behind a token threshold so it only fires when the window is genuinely expensive:
struct MyProfile: LanguageModelSession.DynamicProfile {
let status: Status
var body: some DynamicProfile {
Profile {
Instructions("A conversation between a user and a helpful assistant.")
ToggleDarkModeTool()
}
.summarizeHistory(threshold: 5000, model: summarizerModel)
.rollingWindow(entries: 10)
.droppingCompletedToolCalls()
}
}
Modifiers apply outside-in: tool calls are dropped first, then the rolling window applies, then summarisation runs only if the 10-entry window still exceeds 5,000 tokens. Apple's guidance is explicit that there is no single correct strategy and that composition is the point.
Compaction strategies compared
| Strategy | What it removes | KV cache impact | Best for |
|---|---|---|---|
droppingCompletedToolCalls() |
Finished tool call and tool output pairs before the latest response | Rewrites earlier entries, so the cached prefix is invalidated | Agents that call many tools per turn and never re-read the raw output |
rollingWindow(size: .entries(10)) |
Everything older than the last N entries | Invalidates the prefix each time the window slides | Long chat sessions with no need for early history |
summarizeHistory(threshold: 5000, model:) |
Old entries, replaced by one generated summary | Invalidates the prefix and costs an extra generation call | Sessions where early decisions still matter but verbatim text does not |
onResponse lifecycle trim |
Whatever your closure decides, at session boundaries | Same invalidation, but you control when it happens | Apps that can trim during a natural pause in the UI |
| Append-only (no compaction) | Nothing | Cache preserved, latency lowest | Short sessions that fit the window comfortably |
Apple's own performance chapter in session 242 states the rule plainly: transcript mutations can invalidate key-value caches and raise latency, while appending preserves them. That is the sentence to put in your design review.
Skills: procedural context loaded only when asked for
The second pattern is the one most likely to change how teams structure prompts. A Skill is a named block of instructions that is not in the transcript until the model activates it with a tool call. The README's stated purpose: skills "allow adding extra directions about performing specific tasks into a LanguageModelSession transcript on a just-in-time basis. This prevents context pollution and helps optimize time-to-first-token."
struct CraftingSkills: LanguageModelSession.DynamicInstructions {
var activations: SkillActivations
var body: some DynamicInstructions {
Skills(activations: activations) {
Skill(
name: "origami_folds",
description: "Details about specific types of folds",
prompt: """
Valley Fold: Paper is folded toward you, creating a V-shaped crease
Mountain Fold: Paper is folded away from you, creating an inverted V
...
"""
)
Skill(...)
Skill(...)
}
}
}
SkillActivations conforms to Observable and RandomAccessCollection, so the same object that tracks which skills are live can drive a SwiftUI list showing the user what the assistant just pulled in. That is a real product affordance, not only plumbing.
The initialiser you choose changes the cost. A skill created with prompt: lands in the transcript as a matching tool output, which does not invalidate the key-value cache. A skill created with instructions: is inserted at the end of the first instructions entry, where models weight it more heavily, at the cost of a cache invalidation. Instructions-based skills can also be deactivated later if you pass allowsDeactivation: true, which pairs well with droppingCompletedToolCalls() to clear both the content and the activation record.
| Skill initialiser | Where content lands | Model adherence | Cache cost | Deactivation |
|---|---|---|---|---|
prompt: |
Tool output entry matching the activation call | Normal prompt weighting | No prefix invalidation | Not applicable |
instructions: |
End of the first instructions entry | Higher, models are trained to obey instructions | Prefix invalidated on activation | Optional with allowsDeactivation: true |
@PromptBuilder trailing closure |
Same as prompt: |
Normal prompt weighting | No prefix invalidation | Not applicable |
| No skill, everything in base instructions | Always present | Highest | Paid once at session start | None |
| No skill, injected per prompt | Every prompt | Normal | Grows the prompt every turn | None |
The engineering judgement here is simple. If a body of guidance applies to one task in twenty, a prompt: skill costs nothing until it is needed. If it applies to every turn, putting it in a skill buys you nothing and adds a tool call.
ChatCompletionsLanguageModel: one Swift API, any server
The third piece is the one that turns Apple's API into a general client. ChatCompletionsLanguageModel talks to any server that implements the chat completions REST API, so a locally served open-weight model and Apple's own on-device model sit behind the same LanguageModelSession:
let model = ChatCompletionsLanguageModel(
name: "minimax-m2.5",
url: URL(string: "http://localhost/v1:8000")!,
)
let session = LanguageModelSession(model: model)
let response = try await session.respond(to: "How many folds does it take to make a paper crane?")
print(response.content)
Capabilities are declared, not assumed. Some local servers do not implement guided generation, and you say so at init time:
let model = ChatCompletionsLanguageModel(
name: "minimax-m2.5",
url: URL(string: "http://localhost/v1:8000")!,
supportsGuidedGeneration: false
)
That flag matters because guided generation is how Swift developers get typed output out of the framework, and a server that silently ignores a schema produces a runtime failure that looks like a model quality problem. For the deeper provider story, including how to write your own executor rather than reuse this client, see our walkthrough of the Foundation Models any-LLM provider API.
Where the utilities sit in the WWDC 2026 model line-up
Session 339, "Bring an LLM provider to the Foundation Models framework", shows the swap surface: one line changes which model a session runs against.
import FoundationModels
import MLXFoundationModels
// On-device Apple Foundation Model
let model = SystemLanguageModel()
// Private Cloud Compute model
// let model = PrivateCloudComputeLanguageModel()
// Custom Core AI model
// let model = try await CoreAILanguageModel(resourcesAt: modelURL)
// Open-source MLX model from HuggingFace
// let model = MLXLanguageModel(modelID: "mlx-community/my-model")
let session = LanguageModelSession(model: model)
let response = try await session.respond(to: "...")
print(response.content)
Apple also lists Anthropic and Google partner integrations as upcoming in the same session's introduction chapter. Mike Grabowski, CEO and founder at Callstack, drew the boundary well in his 8 June 2026 write-up of the announcements: "Foundation Models is Apple's model. Core AI is for your model." He added that for app teams "this changes the boundary conditions. A feature can now be evaluated against device class, model availability, latency budget, privacy requirement, and acceptable error rate before it ever becomes a cloud decision."
| Model path | Where inference runs | Marginal token cost | Typical use |
|---|---|---|---|
SystemLanguageModel |
On device | None beyond battery and thermal budget | Short generation, extraction, guided output, tool calling |
PrivateCloudComputeLanguageModel |
Apple's Private Cloud Compute | Free below 2 million first-time App Store downloads per Apple's WWDC 2026 announcement | Longer context and heavier reasoning without a vendor contract |
CoreAILanguageModel |
On device, your weights | None beyond device resources | Domain classifiers, embeddings, specialised vision models |
MLXLanguageModel |
On device via MLX | None beyond device resources | Open-weight models pulled from Hugging Face |
ChatCompletionsLanguageModel |
Any chat completions server | Whatever the host charges, for example $5 and $25 per million tokens for Claude Opus 5 at its 24 July 2026 launch | Frontier models, shared server-side context, existing inference stacks |
Our comparison of Claude Opus 5 and GPT-5.6 Sol for coding agents has the wider hosted-price picture if the last row is where your traffic will land.
The error surface you now have to handle
Opening the API to arbitrary providers forced Apple to standardise failures. Session 339 lists 9 built-in cases that any model can throw:
public enum LanguageModelError: LocalizedError, CustomDebugStringConvertible {
case contextSizeExceeded( )
case rateLimited( )
case refusal( )
case guardrailViolation( )
case unsupportedCapability( )
case unsupportedTranscriptContent( )
case unsupportedGenerationGuide( )
case unsupportedLanguageOrLocale( )
case timeout( )
}
contextSizeExceeded is the one this article is about: Apple's own guidance for it is to trim entries and retry, which is exactly what the compaction modifiers automate. rateLimited and timeout only appear once you point a session at a server, so an app that was on-device-only until now needs new UI states before it ships a ChatCompletionsLanguageModel path.
There is a second failure mode worth wiring up at the same time. By default a thrown tool error reverts the transcript. Session 242 introduces transcriptErrorHandlingPolicy with .revertTranscript and .preserveTranscript, and makes Transcript settable so you can repair it, but only while isResponding is false.
Measure the compaction, do not assume it
Compaction has two costs that do not show up in a code review. Latency rises when the cached prefix is thrown away, and accuracy can fall when rewritten history confuses the model about what was already decided. Apple's answer is instrumentation rather than intuition: session 242 points teams at the Foundation Models Instrument in Xcode for latency and the Evaluations framework for quantifying an accuracy change.
A workable measurement plan before you ship any compaction strategy:
- Record time-to-first-token and tokens-per-second for a fixed 30-turn scripted session with no compaction, as the baseline.
- Re-run the same script with each modifier alone, then in the composed order, and keep the traces.
- Score output quality on the same transcripts with the Evaluations framework so a latency win that costs accuracy is visible.
- Repeat on the lowest-end supported device, because the on-device path is where cache invalidation hurts most.
Custom executors can help here. Session 339 shows how a provider attaches tokensPerSecond and timeToFirstToken as response metadata, so if you write your own executor the numbers arrive with every response rather than only under a profiler.
The Python SDK closes the evaluation loop
Apple ships a second, separate package for the workflow around all this. apple/python-apple-fm-sdk provides Python bindings to the on-device model on macOS, released under Apache-2.0, with 3 releases published and version 0.1.1 dated 8 March 2026 as the latest at the time of writing. It requires macOS 26.0 or later, Xcode 26.0 or later, Python 3.10 or later, and Apple Intelligence enabled on a compatible Mac. Installation is pip install apple-fm-sdk.
Its first listed capability is the useful one for teams: evaluate Swift Foundation Models app features by running batch inference and analysing results from Python, including transcripts exported from Swift apps. Guided generation carries across with a decorator:
import apple_fm_sdk as fm
@fm.generable # This decorator signals this type be generated by a model
class Cat:
name: str
age:int = fm.guide("Age in years", range=(0, 20))
The pairing is deliberate: build the feature in Swift, export the transcript, then run the regression suite in Python where your existing evaluation tooling already lives. WWDC 2026 session 334 covers the same ground alongside an fm command-line tool.
India-specific considerations
Two constraints change the calculus for Indian product teams.
The first is data protection. Under India's Digital Personal Data Protection Act 2023, the safest transcript is the one that never leaves the device. droppingCompletedToolCalls() and rollingWindow are minimisation controls as much as performance controls: tool output containing personal data stops being resident in the session once the modifier drops it. Teams that must keep a server path should treat the summarisation model in summarizeHistory(threshold:model:) as a processor in its own right, because that call sends the old history somewhere to be condensed.
The second is unit economics. On-device and Core AI inference carry no marginal token cost, while a hosted frontier model bills per million tokens, for example the $5 and $25 input and output rates Claude Opus 5 launched with on 24 July 2026. For a price-sensitive consumer app, a chatty feature that runs many short generations per user per day is free on SystemLanguageModel and a recurring line item on a hosted API, and that gap widens with every retained user. The honest counterpoint: the on-device model is small, so quality gating has to happen before the cost saving is real, and Apple's own framing puts Private Cloud Compute in the path when the task needs more capacity or longer context.
Device coverage is the third constraint and it is not solved by any of this. Apple limits its more capable on-device model to higher-end iPhone, iPad and Mac systems, so an Indian user base weighted towards older hardware will exercise the server fallback more often than a US one. Our iOS 27 enterprise fleet readiness guide covers how to work out that split before committing to an architecture.
An adoption checklist
- Pin the dependency at
from: "1.0.0"and read theskills/directory, since Apple wrote it to brief coding agents on the package.
- Start with
droppingCompletedToolCalls()alone. It is the cheapest win in tool-heavy agents and the easiest to reason about.
- Add
rollingWindowonly after you have a measured context-size failure, not in anticipation of one.
- Treat
summarizeHistoryas the last resort. It costs a generation call and it is where subtle behaviour regressions appear.
- Prefer
prompt:-based skills unless the model is ignoring the guidance, then move that one skill toinstructions:and re-measure.
- Set
transcriptErrorHandlingPolicyexplicitly rather than relying on the revert default once tools can fail.
- If you ship a
ChatCompletionsLanguageModelpath, handlerateLimitedandtimeoutin the UI before you handle anything else.
- Declare
supportsGuidedGeneration: falsefor any local server you have not verified, and fail loudly rather than silently.
The real cost is usually the evaluation harness, not the modifiers. The modifiers are three lines.
FAQ
How eCorpIT can help
eCorpIT builds iOS and macOS applications with on-device and hybrid AI features, including transcript design, evaluation harnesses and the device-coverage analysis that decides whether a feature runs locally or falls back to a server path. Our senior engineering teams work under CMMI Level 5 and ISO 27001:2022 practices, and we design applications aligned with DPDP requirements for Indian deployments. If you are planning a Foundation Models feature for iOS 27 and want the latency and accuracy numbers before you commit to an architecture, talk to our iOS engineering team.
References
- apple/foundation-models-utilities on GitHub — package README, Apache-2.0 licence, Skills, history modifiers and ChatCompletionsLanguageModel.
- Build agentic app experiences with the Foundation Models framework, WWDC26 session 242 — dynamic profiles, history transforms, key-value cache guidance.
- Bring an LLM provider to the Foundation Models framework, WWDC26 session 339 — LanguageModel and LanguageModelExecutor protocols, error cases, custom metadata.
- What's new in the Foundation Models framework, WWDC26 session 241 — framework changes announced at WWDC 2026.
- Build AI-powered scripts with the fm CLI and Python SDK, WWDC26 session 334 — command-line and Python workflow.
- Build with the new Apple Foundation Model on Private Cloud Compute, WWDC26 session 319 — the Private Cloud Compute model path.
- Composing dynamic sessions with instructions and profiles — Apple documentation for dynamic profiles.
- apple/python-apple-fm-sdk on GitHub — Python bindings, requirements, releases and guided generation decorator.
- Apple outlines major AI and developer tool updates at 2026 Platforms State of the Union, MacRumors, 9 June 2026 — free Private Cloud Compute access below two million first-time downloads.
- On-device AI after WWDC 2026: what's new, Callstack, 8 June 2026 — Mike Grabowski on the Foundation Models and Core AI boundary.
- apple/coreai-models on GitHub — Core AI model resources referenced by session 339.
- ml-explore/mlx-swift-lm on GitHub — MLX Swift language model package referenced by session 339.
- Foundation Models framework documentation — the base API these utilities extend.
- Anthropic prices Claude Opus 5 at half of Fable 5, Quartz, 24 July 2026 — hosted token pricing used in the cost comparison.
Last updated: 4 August 2026.