Skip to content

feat(ai-providers): add Apple Intelligence on-device provider for macOS 26+#1554

Open
datlechin wants to merge 2 commits into
mainfrom
feat/1048-apple-intelligence-transport
Open

feat(ai-providers): add Apple Intelligence on-device provider for macOS 26+#1554
datlechin wants to merge 2 commits into
mainfrom
feat/1048-apple-intelligence-transport

Conversation

@datlechin
Copy link
Copy Markdown
Member

Closes #1048.

Adds Apple Intelligence as an on-device AI chat provider on macOS 26 and later, backed by the Foundation Models framework. No API key, no network: schema and queries stay on the Mac. The app's macOS 14.0 minimum does not change; everything is gated behind #available(macOS 26, *) and FoundationModels is weak-linked.

What it does

  • New AppleIntelligenceTransport: ChatTransport over LanguageModelSession. It flows through the existing registry, factory, and transport pipeline as a real AIProviderConfig (type: .appleIntelligence, new .device auth style), so the model picker, per-turn attribution, and persistence work with no special-casing.
  • Default for new users on macOS 26+ when the model is available (seeded only when no providers exist, so existing setups are untouched).
  • A pinned Apple Intelligence row in Settings, AI. When the model is unavailable it is greyed with the reason: needs Apple silicon, Apple Intelligence off in System Settings (with a button to open them), model still downloading, or OS older than macOS 26. The row re-checks while the model downloads.
  • One on-device model, so no model list, no reasoning, no image options.

How the hard parts work

  • Streaming: streamResponse yields cumulative snapshots, so the transport diffs each against the previous (with a prefix guard) to emit text deltas.
  • Tools: the chat tools have runtime JSON schemas. Tool.Arguments is GeneratedContent and the GenerationSchema is built at runtime from our JsonValue via DynamicGenerationSchema. The framework runs the tool loop itself; each Tool.call bridges to the existing approval and execution path through toolInvocationRequest and a reply token.
  • Availability: a non-gated AppleIntelligenceStatus facade wraps the #available and SystemLanguageModel.default.availability check so the un-gated UI can read availability without importing the framework.
  • Multi-turn: the transport is stateless and rebuilds a Transcript from the conversation each call, so it needs no reset wiring.

The generic Copilot tool-dispatch functions were renamed to provider-neutral names since this transport reuses them.

Tests

New tests cover the availability facade mapping, the schema bridge, seeding idempotency and no-override, and the factory guard (an Apple Intelligence config never falls back to an OpenAI-compatible transport). Foundation Models tests are gated to macOS 26.

Before merge

  • Needs a build on Xcode 26. The Transcript initializers in AppleIntelligenceTransport.buildTranscript were written against Apple's docs but not compiled here.
  • The System Settings deep link (com.apple.Siri-Settings.extension) is inferred; it falls back to the Settings root if the identifier is wrong. Worth confirming on a real macOS 26 machine.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented Jun 2, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Jun 2, 2026, 5:25 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3667d72492

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

static func makeAppleIntelligenceProvider() -> ChatTransport {
let status = AppleIntelligenceAvailability.currentStatus()
if #available(macOS 26, *), status == .available {
return AppleIntelligenceTransport()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard the FoundationModels-only transport reference

When this target is built with an SDK where canImport(FoundationModels) is false, AppleIntelligenceTransport is compiled out by the #if in its own file, but this factory still references it. The weak-link flags do not help when the SDK module is unavailable, so pre-Xcode 26 builds fail instead of falling back to UnavailableTransport; wrap this branch in #if canImport(FoundationModels) and return the unavailable transport otherwise.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 13e297c8e2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if #available(macOS 26, *), status == .available {
return AppleIntelligenceTransport()
}
return UnavailableTransport(reason: status.statusText)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid caching the unavailable Apple transport

If an existing Apple Intelligence provider is resolved while currentStatus() is transiently unavailable, such as .modelNotReady during the model download, this branch returns an UnavailableTransport; createProvider then caches that transport for the config, so later chats keep failing even after the model becomes available unless the app restarts or the provider settings change. Make this status-dependent fallback bypass the provider cache or invalidate it when availability changes.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AI Chat: Apple Intelligence transport via Foundation Models

1 participant