Skip to content

Add Apple Intelligence language-fallback settings - #844

Open
senadaruc wants to merge 2 commits into
FuJacob:mainfrom
senadaruc:feat/apple-fallback-setting
Open

senadaruc wants to merge 2 commits into
FuJacob:mainfrom
senadaruc:feat/apple-fallback-setting

Conversation

@senadaruc

@senadaruc senadaruc commented Oct 2, 2026 •

Copy link
Copy Markdown

Summary

When Apple Intelligence rejects a language (for example Macedonian), SuggestionEngineRouter silently retries with the Open Source model. Users had no way to see or control that. This adds two switches under Engine & Model → Apple Intelligence:

  • Fall Back to Open Source Model: on by default, which is today's behavior. Off returns an empty result with suppressionReason: "appleLanguageUnsupported" and never touches the local model, so unsupported languages get no suggestion and no multi-GB model load.
  • Keep Fallback Model Loaded: off by default. When on, and while the fallback is on, AppDelegate.startRuntimeIfPreferredEngineRequiresIt keeps the local runtime started under Apple Intelligence. Without it, the first fallback suggestion waits for the GGUF to load on demand (LlamaRuntimeManager.preparedRuntime()).

The rows name the model the fallback uses and warn when no Open Source model is selected. The fallback always uses the selected Open Source model, because the local runtime holds one model at a time and a separate fallback model would mean swapping weights mid-typing. AppDelegate now re-evaluates the runtime when either switch changes, as it already did for engine changes.

Persisted as cotabbyAppleLanguageFallbackEnabled (default true) and cotabbyKeepFallbackModelLoaded (default false) in the engine settings domain, and registered in SettingsIndex so search finds them.

Validation

xcodebuild test -workspace build/cotabby-dependencies/Cotabby.xcworkspace -scheme Cotabby \
  -destination 'platform=macOS' -derivedDataPath build/DerivedData CODE_SIGNING_ALLOWED=NO
Executed 2647 tests, with 14 tests skipped and 0 failures

New router tests:

  • with the fallback off, the local model never runs and the result is empty with the new suppression reason
  • both settings default to today's behavior and persist

The existing fallback tests still pass unchanged.

Not run by hand yet in a signed build; swiftlint --strict not run.

Risk / rollout notes

  • No behavior change for existing users: the fallback stays on and keep-loaded stays off by default.
  • With keep-loaded on, the Open Source model's memory (several GB) stays resident while Apple Intelligence is selected. The setting's description says so.

🤖 Generated with Claude Code

RetriggerConfidence Score: 4/5

The PR is not yet safe to merge because changing the fallback model can retain several GB of model memory despite keep-loaded being off.

Fix All in CodexFindings

  1. P1 Picker loads fallback model ▶
  2. P2 Unsupported languages go uncounted ▶
  3. P2 Keep-loaded search targets wrong switch ▶

Summary

The PR adds persisted Apple Intelligence language-fallback and keep-loaded controls, exposes the shared Open Source model as the fallback choice, updates runtime startup, and supports discovery through a symlinked models folder.

  • The new fallback-model picker can load and retain the local model despite the keep-loaded setting being off.
  • The earlier unsupported-language accounting and keep-loaded search-target findings remain unresolved.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Apple Intelligence rejects language] --> B{Fallback enabled?}
  B -- No --> C[Empty suppressed result]
  B -- Yes --> D[Selected Open Source model]
  D --> E[Generate fallback suggestion]
  F[Keep loaded enabled] --> G[Prepare local runtime in advance]
Loading

Reviews (2) · Last reviewed commit: "Pick the fallback model in the Apple Int..."

Engine & Model → Apple Intelligence gains two switches:

- Fall Back to Open Source Model (on by default, today's behavior): when
  Apple Intelligence rejects the text's language, the router retries with
  the selected Open Source model. Off returns no suggestion
  ("appleLanguageUnsupported") and never loads the local model.
- Keep Fallback Model Loaded (off by default): keeps the local runtime
  started while Apple Intelligence is selected, so the first fallback
  suggestion does not wait for a multi-GB model to load.

The row names the model the fallback uses and warns when no Open Source
model is selected. The fallback always uses the selected Open Source model
because the local runtime holds one model at a time. AppDelegate now
re-evaluates the runtime when either switch changes, as it does for an
engine change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 10 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 57c21e2b-8717-4925-a214-190d866af3b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7724926 and 2a3803f.

📒 Files selected for processing (10)
  • Cotabby/App/Core/AppDelegate.swift
  • Cotabby/Models/Settings/SuggestionSettingsData.swift
  • Cotabby/Models/Settings/SuggestionSettingsModel.swift
  • Cotabby/Services/Runtime/SuggestionEngineRouter.swift
  • Cotabby/Support/Runtime/BundledRuntimeLocator.swift
  • Cotabby/Support/Settings/SuggestionSettingsStore.swift
  • Cotabby/UI/Settings/Panes/Engine/EngineAndModelPaneView+AppleIntelligence.swift
  • Cotabby/UI/Settings/SettingsIndex.swift
  • CotabbyTests/Services/Runtime/SuggestionEngineRouterTests.swift
  • CotabbyTests/Support/Runtime/BundledRuntimeLocatorTests.swift
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment on lines +72 to +75
return SuggestionResult(
generation: request.generation, rawText: "", text: "", latency: 0,
suppressionReason: "appleLanguageUnsupported"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Unsupported languages go uncounted

When Apple Intelligence rejects a language and fallback is off, this return skips the router's quality accounting. The coordinator also skips counting it because the result has a suppression reason. As a result, these requests are missing from both the generated count and the appleLanguageUnsupported suppression count.

Suggested change
return SuggestionResult(
generation: request.generation, rawText: "", text: "", latency: 0,
suppressionReason: "appleLanguageUnsupported"
)
let result = SuggestionResult(
generation: request.generation, rawText: "", text: "", latency: 0,
suppressionReason: "appleLanguageUnsupported"
)
recordQualityOutcome(result)
return result

Fix in Codex Fix in Claude Code

Comment on lines +41 to +52
Toggle(isOn: Binding(
get: { suggestionSettings.keepsFallbackModelLoaded },
set: { suggestionSettings.setKeepsFallbackModelLoaded($0) }
)) {
SettingsRowLabel(
title: "Keep Fallback Model Loaded",
description: "Load the fallback model in advance so its first suggestion doesn't wait for it " +
"to load. Uses the model's memory (several GB) while Apple Intelligence is selected.",
systemImage: "memorychip"
)
}
.disabled(!suggestionSettings.isAppleLanguageFallbackEnabled)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Keep-loaded search targets wrong switch

“Keep Fallback Model Loaded” has no search item or row anchor. Its search terms, including “keep loaded” and “preload,” belong to the fallback switch's item, so searching for the new setting highlights the other switch instead. Give the keep-loaded switch its own indexed item and anchor so search can take users directly to it.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex Fix in Claude Code

…s in a linked folder

- The Open Source section is hidden while Apple Intelligence is the
  engine, so the fallback model could not be chosen anywhere visible.
  The Apple Intelligence section now has a Fallback Model picker (the
  shared Open Source selection, since the runtime holds one model). The
  warning now reports "no downloaded models" instead of reading the
  stopped runtime's selection, which is nil while it is unloaded.
- Model discovery resolves a symlinked models folder before walking it:
  FileManager.enumerator does not descend into a symlinked root, so a
  folder linked to an external drive or another location listed nothing
  and the app cleared the saved model selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
.font(.caption)
.foregroundStyle(.orange)
} else {
Picker(selection: selectedModelBinding) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Picker loads fallback model

When “Keep Fallback Model Loaded” is off, changing this picker still calls runtimeModel.selectModel, which prepares and loads the selected GGUF. The model can then remain in memory while Apple Intelligence is selected, despite the user choosing not to keep it loaded.

Fix in Codex Fix in Claude Code

This branch has not been deployed

No deployments
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.

1 participant