Skip to content

Add Apple's on-device Foundation Models (iOS 26+) as an AI Coach provider, with no key and no network #368

Description

@auto-mo

What

Add Apple's Foundation Models framework (iOS 26+) as a provider option in the AI Coach
settings, alongside the existing OpenAI-compatible ones. Selecting it would run the coach
entirely on-device: no base URL, no API key, no network call, nothing leaving the phone.

Why this is worth the work

The coach is the one part of the app that requires either a paid API key or a self-hosted
server. Everything else is free and local by default. This closes that gap for iOS 26 users.

It also removes a network egress path. PRIVACY.md is careful to list the BYOK assistant as one
of the few things that sends health data off the device, with the honest warning that "the
prompts contain your health data." An on-device provider makes that warning unnecessary for the
people who choose it.

It works, I measured it

I couldn't call the framework directly from another app, because iOS suspends a backgrounded
server process and app-to-app localhost serving is a dead end. So I tested Apple's model
through the OpenAI shim in the App Store app "Local LLM Server", from a
Mac on the same LAN, on an iPhone 17 Pro / iOS 26:

  • Tool call emission: correct. Returned a well-formed tool_calls object with a valid
    function name and JSON arguments, from a tools array supplied in the request.
  • Second turn: correct. Given the tool result, it produced a coherent answer that used the
    actual returned values.
  • Latency for that two-turn loop: 1.4 seconds.

For comparison, on the same day I benchmarked qwen2.5:7b-instruct-q4_K_M on a self-hosted
mini-PC and got 23.5s per turn, which put a multi-tool coach question past this app's 120s
client timeout. On-device was roughly thirty times faster than that, free, and never left the
phone.

Why it fits this project's rules specifically

Foundation Models does guided generation. Tool schemas are enforced at the token-sampling
level, so the model structurally cannot emit malformed JSON for a declared tool. That is a
better guarantee than an unconstrained GGUF model gives, and it sits well with the project's
"never fabricate a number" rule.

Anecdotally relevant: I tested qwen2.5:3b as a cheap local coach and it invented a recovery
percentage that was nowhere in the tool output, and twice described an elevated resting heart
rate as being below baseline. Constrained decoding does not fix reasoning, but it does remove
the malformed-output class of failure entirely.

Scope

As I read the tree this lands in lib/ai/ as an additional provider, plus a platform-channel
bridge to a small Swift shim calling LanguageModelSession with GenerationSchema built from
the existing tool definitions. Nothing in protocol or analytics is touched, and no existing
provider changes. Android and older iOS are unaffected. The option would only appear where the
framework is available and Apple Intelligence is enabled.

Honest limitations

  • iOS 26+ only, and only where Apple Intelligence is enabled and the model has downloaded.
  • The on-device model is roughly 3B. Expect weaker reasoning than a frontier API model. This
    should be presented as the free/private option, not the best one.
  • What I could not test: a real multi-tool agentic loop of the shape this app's coach
    actually runs. My test was two turns with a single tool. Longer loops and how it handles
    several tools in one request are unknown to me.
  • I also have not verified how the framework behaves under memory pressure while the app is
    also holding a BLE connection and the local database.

Prior art

I searched issues and PRs across edge, analytics, protocol and backend for foundation
models, Apple Intelligence, CoreML, MLX, on-device LLM. I found nothing, so apologies if this
has been discussed somewhere I did not look, Discord for instance.

Offer

Happy to implement this if you want it. Asking first, per CONTRIBUTING, rather than arriving
with an unsolicited PR of this size. If you would rather not take on an Apple-specific
dependency, or it conflicts with the F-Droid / dependency-isolation direction in #321, that is
a completely reasonable no and I would rather hear it now.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions