Apple Foundation Models on the command line
There is a language model built into my Mac mini, and with macOS 27 I can talk to it straight from Terminal. No account, no API key, no per-token cost.
What AFM is
Apple's own write-up, Introducing the Third Generation of Apple's Foundation Models, describes a family of five models. Two run on the device:
- AFM 3 Core is a 3-billion-parameter dense model. It is the everyday workhorse.
- AFM 3 Core Advanced is a 20-billion-parameter sparse model that only activates 1 to 4 billion parameters per request. Apple says it is unlocked on its most capable Apple silicon systems, and that it is natively multimodal, which is what powers features like higher-accuracy dictation.
The other three (AFM 3 Cloud, AFM 3 Cloud Pro and an image model) run on Apple's Private Cloud Compute servers instead of on the Mac.
AFM is not purely an Apple model. Apple says the third generation was custom-built in collaboration with Google, and coverage of the announcement describes it as built on the technology behind Gemini, adapted for Apple silicon. The largest cloud tier even runs on NVIDIA GPUs in Google Cloud under Private Cloud Compute. The on-device models are the part that stays on your Mac.
Which Macs can use it
The command line tool, fm, is pre-installed with macOS 27 (Golden Gate, released September 14, 2026). So the question is mostly which Macs can run macOS 27, and that is Apple silicon only:
- MacBook Air (2020 and later) and MacBook Pro (2020 and later)
- Mac mini (2020 and later) and iMac (2021 and later)
- Mac Studio (2022 and later) and Mac Pro (2023)
- MacBook Neo
Intel Macs are out entirely. Two caveats:
- M1 and M2 Macs run macOS 27 but may not get the biggest on-device model. One compatibility tracker puts the cutoff for AFM 3 Core Advanced at an M3 or newer with 12 GB of memory. Apple only says "most capable Apple silicon systems", so treat the exact line as approximate.
- macOS 26 (Tahoe) has the model but not the command. Apple Intelligence Macs on Tahoe can use the on-device model through the Foundation Models framework in apps and the "Use Model" action in Shortcuts. There is just no
fmin Terminal.
My machine is an M4 Mac mini with 16 GB, running macOS 27, which clears every bar above.
Using it from Terminal
First-time setup: accept the license
Open Terminal and run this once before anything else:
sudo fm license
It asks for your Mac password (that is the sudo), then shows the Legal Notice & Terms. Type yes to accept. You only need to do this the first time, and fm will not respond to prompts until it is done.
Now type fm on its own. You get a list of subcommands:
respondfor a one-shot answer, which is the one you script withchatfor an interactive sessionschemato define structured JSON outputcount-tokensto see how much of the context window a prompt will useavailableto check that the model is readyserveto start a local Chat Completions API serverlicenseto read and accept the terms (the step above)
Try it
First, check that the model is there:
$ fm available
System model available
Then ask it something:
$ fm respond "Explain what a launchd agent is in two sentences."
A launchd agent is a process that runs in the background on a macOS or iOS device to perform specific tasks at defined intervals or in response to system events. It is typically managed through the launchd service and can be configured to start, stop, or monitor certain operations.
And see what that prompt cost against the context window:
$ fm count-tokens "Explain what a launchd agent is in two sentences."
12
Getting your own text in (the gotcha)
My first attempt at a summarizer was the obvious one: pipe a file in and give the prompt as an argument. The sample notes below are made up.
$ cat notes.txt | fm respond "Summarize this in one sentence."
The text discusses the importance of communication in the workplace.
That sentence is not in the notes. When a prompt argument is present, the piped text never reaches the model, and the model happily answered about nothing. There is no error, which is the dangerous part. A small model will make something plausible up rather than say it saw no input.
There are two ways that do work. Pipe the text on its own and put the task in --instructions:
$ cat notes.txt | fm respond --instructions "Summarize the text you are given in one sentence."
Beta shipped Tuesday, two open bugs in sync, next review Friday, vendor contract renewal to confirm.
Or pass the text explicitly with --text:
$ fm respond "Summarize this in one sentence." --text "$(cat notes.txt)"
Beta shipped Tuesday, two bugs open, next review Friday, vendor contract renewal needed.
If a summary looks too generic, suspect the plumbing before the model. A quick test is to put a made-up fact in the input and check that it comes back out.
Structured output for scripts
The feature that turns this from a chat toy into a building block is the schema. You describe the shape of the answer, and fm returns JSON that matches it. fm schema writes the schema file:
$ fm schema object --name ActionItems --string items --array > schema.json
$ cat schema.json
{
"additionalProperties" : false,
"title" : "ActionItems",
"properties" : {
"items" : {
"type" : "array",
"items" : {
"type" : "string"
}
}
},
"x-order" : [
"items"
],
"required" : [
"items"
],
"type" : "object"
}
Then hand it to respond:
$ fm respond "List the action items in these notes." --text "$(cat notes.txt)" --schema schema.json
{"items": ["Ship the beta on Tuesday.", "Address two open bugs, both in sync.", "Schedule the next review for Friday.", "Confirm the vendor contract renewal."]}
The JSON shape is guaranteed, which makes it safe to pipe into jq or a script. The content is not: it listed "ship the beta" as an action item even though the notes say that already happened. Treat the output as a first draft.
Apple's own demo uses the same pattern to sort a folder into final and draft files by name, which is exactly the kind of fuzzy job that is miserable to write with regular expressions.
Other options
fm respond --help lists more than I have used. The ones worth knowing about:
--image(with--label) to include an image in the prompt--toolto turn on built-in tools, currentlybarcodeandocr--use-case content-taggingfor classification-style jobs--save-transcriptand--resumeto keep a conversation going between runs--greedyfor repeatable, less random outputfm serve, which starts a local Chat Completions-style server, with a--socketoption for local scripts
Apple's WWDC demo shows a --model pcc option for the larger Private Cloud Compute model. On my machine, fm respond --help lists only the on-device system model, so everything on this page ran locally and I have not used the cloud option.
How fast it is
I timed it on the Mac mini (M4, 16 GB, macOS 27.0.1, plugged in) by running fm serve on a Unix socket, so I could stream the replies and read the server's own token counts. Every prompt is made up. Each one ran once to warm up and then five times; the numbers are medians.
| Prompt | Tokens in | Tokens out | First token | Total | Tokens per second |
|---|---|---|---|---|---|
| One-sentence answer | 69 | 29 | 0.34 s | 0.86 s | 34 |
| About 120 words | 74 | 149 | 0.39 s | 3.6 s | 41 |
| About 300 words | 87 | 627 | 0.43 s | 13.8 s | 46 |
| Structured JSON from a short note | 154 | 75 | 1.8 s | 1.8 s | 42 |
| One sentence about a 570-token text | 571 | 35 | 0.62 s | 1.0 s | 34 |
What the numbers say:
- Once it is going, it writes about 46 to 47 tokens per second (that is what the two longer replies work out to). Short replies look slower per second only because the first third of a second is fixed.
- The first token takes 0.3 to 0.4 seconds for a short prompt and about 0.6 seconds with 570 tokens of input.
- Input is not free. Prompts with a one-word answer took 1.1 seconds at 900 tokens, 3.8 seconds at 4,200 and about 10 to 12 seconds at 8,000.
- It is steady. Five runs of the same prompt landed within about 2 percent of each other, and with the temperature set to 0 the five replies were word-for-word identical.
- Structured output does not stream. The whole JSON arrives in one piece after about 1.8 seconds. The server counted 75 output tokens for it although the text itself is 49 tokens; I assume the difference is the structure the model has to fill in, but I did not confirm that.
- For plain text the server's count is only a little above what
fm count-tokensgives for the reply (627 against 624 for the longest one).
Limits worth knowing
- The on-device context window is small, and I measured where it ends. A prompt of 8,169 tokens (instructions included) with a one-word answer worked, and one of 8,211 was refused in about two seconds with
ExceededContextWindowSizeError. So the limit is about 8,200 tokens. I did not test what happens when a long reply runs into it, but expect the reply to come out of the same budget: a 300-word answer is roughly 600 tokens. Usefm count-tokensto check a prompt before you send it. - It is a small model. It is good at classifying, extracting, summarizing and reformatting, and it is not a replacement for a frontier model on hard reasoning.
- The weights are not open or portable, so it is a poor fit for work that has to be reproducible on another machine.
Where I already use it
The voice memo router from the Voice memos that file themselves lab report transcribes each recording locally on the Mac, with no cloud service and no cost. That transcription step is a macOS Shortcut, and the Shortcut runs on AFM. So the same model family that answers fm respond is the one turning my spoken memos into text.
The natural next step is to pass each transcript through fm respond with a schema, to pull out a title, a topic and any action items, all still on my own hardware.
Where it ended up
I now have a free, private, always-available language model one keystroke away, and it slots into the same shell scripts and launchd jobs I already run. For small jobs it is the first thing I reach for. For anything long or hard, I still use a bigger model, and I check its output against the input before I trust it.
Sources: Apple Machine Learning Research and WWDC26 session 334, Build AI-powered scripts with the fm CLI and Python SDK. There is also a Python SDK, apple_fm_sdk, if you would rather script it from Python.
This is entry 24 in the AI Collaboration log.