Apple Foundation Models vs a Deterministic Stats Layer: Why On-Device Is Not the Same as Showing Your Work

"It runs on the device" answers where the compute happened. It does not answer whether the number is traceable, or whether asking twice gives you the same answer. Those are different promises, and 2026 is full of apps quietly treating them as one.

By MetricBridge · Updated 11 October 2026 · ~7 min read

AI-assisted, human reviewed. How we use AI

Quick answer. Running a language model on the iPhone and computing a health number on the iPhone are two different engineering achievements. Apple's Foundation Models framework gives apps a free, offline 3 billion parameter model with a 4,096 token window per session. That model still predicts tokens. A deterministic stats layer does what the model cannot: it runs arithmetic over your recorded rows, so the same input always returns the same output and every figure can point at the days it came from. MetricBridge puts the second one in front and lets a model only phrase the result.

What the Foundation Models framework actually gives you

Credit first, because this is genuinely good infrastructure. Since iOS 26, iPadOS 26 and macOS 26, Apple has exposed the on-device model at the core of Apple Intelligence to third-party apps. Per Apple's own documentation and newsroom post:

  • A 3 billion parameter model, on device. Inference is free of cost to the developer and works offline.
  • Guided generation. The @Generable macro lets you declare a Swift struct and get "strong guarantees" that the model returns an instance of your type rather than raw prose.
  • Tool calling. A Tool is a call the model makes back into your app, for example fetching rows from a local database. Apple's own example is a tool that grounds responses in the app's data rather than the model's memory.
  • A hard context window. Apple documents 4,096 tokens per LanguageModelSession, and instructions, prompts, tool definitions, generable schemas and outputs all count against it. Exceed it and the framework throws LanguageModelError.contextSizeExceeded.

Notice what is on that list and what is not. Guided generation guarantees the shape of the output. Tool calling guarantees the model can ask your code for data. Neither guarantees that a number in the output was computed rather than generated. Schema-valid and true are not the same property, and a struct full of plausible floats passes every check the framework offers.

A 3B model is still a model

The instinct is that a smaller local model is a safer model. For privacy, true. For arithmetic, no. A transformer samples from a probability distribution over the next token. Ask it for the average of seven values and it produces a string that looks like an average, because strings like that appeared in its training distribution. Run it twice and you can get 49.6 and 49.8. Both are formatted correctly. Neither is a function of your data.

This is not a small-model problem you fix by waiting for a bigger on-device model, and the 2026 rebuild does not change it. It is the architecture. The response Apple itself sketches with the Tool protocol is the right one: do not ask the model to compute, ask it to call something that computes.

The one-line distinction. A model that knows your average is guessing in a convincing accent. A model that calls something which computed your average is safe, and the thing it called is the part you should be evaluating.

What a deterministic stats layer looks like

MetricBridge's compute layer is a plain Swift file, AskEngine.swift. Its header states the rule directly: the LLM never computes numbers; every figure a user sees comes from that file. The engine parses a question into a tool call (the same 16 tool names the MCP server exposes: get_health_metrics, get_trends, compare_periods, get_freshness, get_intraday, and so on), resolves the metric name, runs the aggregation, and returns a struct called ComputedFacts.

That struct is the whole argument. It carries the canonical metric name, the unit, a range label, the count of days that actually have data, the raw (day, value) rows, the headline aggregate and its label, any secondary figures, and a direction. It also carries two fields that exist purely for honesty:

  • coverageDays next to dayCount. A "most recent value" answer displays one day, but confidence should follow the whole series. Without that split the app called years of history "only a few days of data".
  • unevenWindows. When a comparison's two sides hold a different number of recorded days, the layer says so instead of printing span totals beside a per-day rate percentage.

Those fields are the tell. A generative layer has no reason to volunteer a caveat that weakens its own answer. A compute layer has no opinion to protect.

The provenance card, and how to reproduce it

Every answer in the app shows a provenance strip: the metric, the date range and the sample count, opening to the daily rows behind the number. Here is that card from a reference implementation of the same fields, on a seeded 30 day series. The script is committed at scripts/bench_deterministic_layer.py:

{
  "metric": "heart_rate_variability",
  "title": "Heart Rate Variability",
  "unit": "ms",
  "rangeLabel": "2026-10-05 to 2026-10-11",
  "dayCount": 7,
  "coverageDays": 30,
  "headline": 49.6,
  "headlineLabel": "7 day average",
  "secondary": { "change_vs_prior_14_days_pct": 16.3 },
  "direction": "up",
  "rows": [ /* 30 day and value pairs, 2026-09-12 to 2026-10-11 */ ]
}

Two runs, two hashes:

$ python3 scripts/bench_deterministic_layer.py
bytes      : 1651
sha256     : f2e87621b46b2fe04cd81a86cb2183ed2e271237ca8d23b9123171445359d9d7
headline   : 49.6 ms (7 day average)
secondary  : 16.3 % up vs prior 14 days (mean 42.6 ms)

run 2 sha256: f2e87621b46b2fe04cd81a86cb2183ed2e271237ca8d23b9123171445359d9d7 (identical)
figures computed by the layer : 5 [49.6, 16.3, 42.6, 7, 30]
figures computed by the model : 0

That is the claim in five lines: the layer produced five figures by arithmetic, the model produced zero. The card also keeps the 7 against the 30, seven days displayed and thirty days behind the answer, rather than collapsing them into one flattering number.

Side by side

QuestionA generative on-device model aloneA deterministic stats layer
Where does the number come from?Predicted tokensArithmetic over recorded rows
Same question twiceCan differ; sampling is stochasticByte-identical output
Can it show its work?Can describe reasoning; cannot prove the figureEmits the rows, day counts and range
What limits a long history?4,096 tokens per sessionNothing; the model gets a short summary
Volunteers a caveat?No reason toReports sparse data and uneven windows
Data leaves the phone?No, for on-device callsNo, by construction

Where the framework wins anyway

Plenty of places. Guided generation removes a whole class of parsing bugs, and a @Generable schema is a better contract than a regex over prose. Summarizing a Weekly Brief into a paragraph, turning a metric name into a readable sentence, classifying a rambling question into one of 16 tools: these are generation tasks and the on-device model is free, private and good enough. The mistake is handing it the arithmetic as well, because that is the part it cannot do and the part a reader will check.

There is a fair critique of our side too. A deterministic layer is only as good as what it is handed, and HealthKit is sparse: a wrist temperature sensor records only while you sleep, and a metric can vanish for a week. Determinism gives the same honest answer every time. It does not conjure data that was never recorded.

What we are not claiming

None of this diagnoses, detects, treats or monitors any condition, and no provenance card changes that. Where a metric moves away from your recent range, the language is a deviation from your personal baseline, never abnormal. This is data plumbing and wellness information about your own numbers, not medical advice. Being able to trace a figure does not make that figure a diagnosis.

Answers that show their numbers, on the phone

Ask your Apple Health data anything and open the provenance strip behind every answer, then export 190 metrics as local JSON to your own agents over a read-only MCP server that makes zero network calls. One purchase covers the whole product: $24.99 one-time, no subscription. There is a free trial first, and the App Store shows the current price.

Frequently asked questions

Does using Apple Foundation Models mean my health data stays on my iPhone?

Only if the app is built that way. Apple's on-device model runs locally and Apple states inference is free of cost and available offline, which is a real privacy property. But the framework also gives apps access to Private Cloud Compute models, and availability depends on the device supporting Apple Intelligence. On-device is a property of the specific call, not a sticker on the app.

Why can't an on-device language model just calculate my average?

Because a language model predicts the next token, it does not evaluate arithmetic. The same question can produce 49.6 on one run and 49.8 on the next, and neither number is traceable to a row in your data. A deterministic compute layer runs the actual formula over the recorded values, so the output is a function of the input and nothing else.

What is inside a provenance card?

The metric and its unit, the range label, the number of days that actually have data, the raw day and value rows behind the answer, the headline aggregate and its label, and any secondary number such as the change against a prior window. In MetricBridge the strip is visible on the answer and opens to show the daily samples the number was built from.

Is a deterministic number always correct?

No. Determinism means the same input gives the same output, not that the input is complete or the interpretation is right. If only three days have data, the answer says so and quotes the day count instead of hiding it. If the two windows a comparison covers hold a different number of recorded days, the layer reports that too. Determinism makes an error visible; it does not make it impossible.

What is the 4,096 token context window limit for Apple's on-device model?

Apple documents a 4,096 token window per LanguageModelSession, and the sum of instructions, prompts, tool definitions, generable schemas and outputs all count against it. Once a session exceeds the limit the framework throws a contextSizeExceeded error. That is one more reason a year of health data should be computed locally and handed to the model as a short summary, not as raw tokens.

Does MetricBridge use Apple Foundation Models?

MetricBridge answers questions about your Apple Health data on the iPhone using a deterministic compute layer, and every figure shown comes from that layer, not from a model. It has no account, exports 190 metrics as local JSON, and its open-source MCP server makes zero network calls. Where a model narrates, the narration replays numbers it was given and is never the source of a number.

MetricBridge · Apple Health → your AI agent, privately. · Home · Privacy · Terms · Support