fix: prevent crash on macos 26.x beta during startup (#473)
defer apple intelligence availability check from app initialization to when the user actually tries to use the feature. accessing SystemLanguageModel.default during early app startup causes SIGABRT on macOS 26.x (tahoe) beta. - remove availability check from default_post_process_providers() - always include apple intelligence provider on macos arm64 - add checkAppleIntelligenceAvailable command for lazy checking - show error message if apple intelligence unavailable when selected - clear error on any dropdown selection for better UX
This commit is contained in:
parent
f7e73fc602
commit
d3a0281062
17 changed files with 109 additions and 30 deletions
2
.github/workflows/pr-test-build.yml
vendored
2
.github/workflows/pr-test-build.yml
vendored
|
|
@ -4,7 +4,7 @@ on:
|
|||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to build'
|
||||
description: "PR number to build"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
|
|
|
|||
|
|
@ -115,3 +115,18 @@ pub fn open_app_data_dir(app: AppHandle) -> Result<(), String> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if Apple Intelligence is available on this device.
|
||||
/// Called by the frontend when the user selects Apple Intelligence provider.
|
||||
#[specta::specta]
|
||||
#[tauri::command]
|
||||
pub fn check_apple_intelligence_available() -> bool {
|
||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
||||
{
|
||||
crate::apple_intelligence::check_apple_intelligence_availability()
|
||||
}
|
||||
#[cfg(not(all(target_os = "macos", target_arch = "aarch64")))]
|
||||
{
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ pub fn run() {
|
|||
commands::open_recordings_folder,
|
||||
commands::open_log_dir,
|
||||
commands::open_app_data_dir,
|
||||
commands::check_apple_intelligence_available,
|
||||
commands::models::get_available_models,
|
||||
commands::models::get_model_info,
|
||||
commands::models::download_model,
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ pub struct PostProcessProvider {
|
|||
pub id: String,
|
||||
pub label: String,
|
||||
pub base_url: String,
|
||||
#[serde(default)]
|
||||
pub allow_base_url_edit: bool,
|
||||
#[serde(default)]
|
||||
pub models_endpoint: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Type)]
|
||||
|
|
@ -374,45 +378,63 @@ fn default_post_process_providers() -> Vec<PostProcessProvider> {
|
|||
id: "openai".to_string(),
|
||||
label: "OpenAI".to_string(),
|
||||
base_url: "https://api.openai.com/v1".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
},
|
||||
PostProcessProvider {
|
||||
id: "openrouter".to_string(),
|
||||
label: "OpenRouter".to_string(),
|
||||
base_url: "https://openrouter.ai/api/v1".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
},
|
||||
PostProcessProvider {
|
||||
id: "anthropic".to_string(),
|
||||
label: "Anthropic".to_string(),
|
||||
base_url: "https://api.anthropic.com/v1".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
},
|
||||
PostProcessProvider {
|
||||
id: "groq".to_string(),
|
||||
label: "Groq".to_string(),
|
||||
base_url: "https://api.groq.com/openai/v1".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
},
|
||||
PostProcessProvider {
|
||||
id: "cerebras".to_string(),
|
||||
label: "Cerebras".to_string(),
|
||||
base_url: "https://api.cerebras.ai/v1".to_string(),
|
||||
},
|
||||
PostProcessProvider {
|
||||
id: "custom".to_string(),
|
||||
label: "Custom".to_string(),
|
||||
base_url: "http://localhost:11434/v1".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
},
|
||||
];
|
||||
|
||||
// Note: We always include Apple Intelligence on macOS ARM64 without checking availability
|
||||
// at startup. The availability check is deferred to when the user actually tries to use it
|
||||
// (in actions.rs). This prevents crashes on macOS 26.x beta where accessing
|
||||
// SystemLanguageModel.default during early app initialization causes SIGABRT.
|
||||
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
|
||||
{
|
||||
if crate::apple_intelligence::check_apple_intelligence_availability() {
|
||||
providers.push(PostProcessProvider {
|
||||
id: APPLE_INTELLIGENCE_PROVIDER_ID.to_string(),
|
||||
label: "Apple Intelligence".to_string(),
|
||||
base_url: "apple-intelligence://local".to_string(),
|
||||
});
|
||||
}
|
||||
providers.push(PostProcessProvider {
|
||||
id: APPLE_INTELLIGENCE_PROVIDER_ID.to_string(),
|
||||
label: "Apple Intelligence".to_string(),
|
||||
base_url: "apple-intelligence://local".to_string(),
|
||||
allow_base_url_edit: false,
|
||||
models_endpoint: None,
|
||||
});
|
||||
}
|
||||
|
||||
// Custom provider always comes last
|
||||
providers.push(PostProcessProvider {
|
||||
id: "custom".to_string(),
|
||||
label: "Custom".to_string(),
|
||||
base_url: "http://localhost:11434/v1".to_string(),
|
||||
allow_base_url_edit: true,
|
||||
models_endpoint: Some("/models".to_string()),
|
||||
});
|
||||
|
||||
providers
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -343,6 +343,13 @@ async openAppDataDir() : Promise<Result<null, string>> {
|
|||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Check if Apple Intelligence is available on this device.
|
||||
* Called by the frontend when the user selects Apple Intelligence provider.
|
||||
*/
|
||||
async checkAppleIntelligenceAvailable() : Promise<boolean> {
|
||||
return await TAURI_INVOKE("check_apple_intelligence_available");
|
||||
},
|
||||
async getAvailableModels() : Promise<Result<ModelInfo[], string>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("get_available_models") };
|
||||
|
|
@ -635,7 +642,7 @@ export type ModelLoadStatus = { is_loaded: boolean; current_model: string | null
|
|||
export type ModelUnloadTimeout = "never" | "immediately" | "min_2" | "min_5" | "min_10" | "min_15" | "hour_1" | "sec_5"
|
||||
export type OverlayPosition = "none" | "top" | "bottom"
|
||||
export type PasteMethod = "ctrl_v" | "direct" | "none" | "shift_insert" | "ctrl_shift_v"
|
||||
export type PostProcessProvider = { id: string; label: string; base_url: string }
|
||||
export type PostProcessProvider = { id: string; label: string; base_url: string; allow_base_url_edit?: boolean; models_endpoint?: string | null }
|
||||
export type RecordingRetentionPeriod = "never" | "preserve_limit" | "days_3" | "weeks_2" | "months_3"
|
||||
export type ShortcutBinding = { id: string; name: string; description: string; default_binding: string; current_binding: string }
|
||||
export type SoundTheme = "marimba" | "pop" | "custom"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useSettings } from "../../../hooks/useSettings";
|
||||
import { useSettingsStore } from "../../../stores/settingsStore";
|
||||
import type { PostProcessProvider } from "@/bindings";
|
||||
import { commands, type PostProcessProvider } from "@/bindings";
|
||||
import type { ModelOption } from "./types";
|
||||
import type { DropdownOption } from "../../ui/Dropdown";
|
||||
|
||||
|
|
@ -12,6 +11,7 @@ type PostProcessProviderState = {
|
|||
selectedProvider: PostProcessProvider | undefined;
|
||||
isCustomProvider: boolean;
|
||||
isAppleProvider: boolean;
|
||||
appleIntelligenceUnavailable: boolean;
|
||||
baseUrl: string;
|
||||
handleBaseUrlChange: (value: string) => void;
|
||||
isBaseUrlUpdating: boolean;
|
||||
|
|
@ -60,6 +60,8 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
|
|||
}, [providers, selectedProviderId]);
|
||||
|
||||
const isAppleProvider = selectedProvider?.id === APPLE_PROVIDER_ID;
|
||||
const [appleIntelligenceUnavailable, setAppleIntelligenceUnavailable] =
|
||||
useState(false);
|
||||
|
||||
// Use settings directly as single source of truth
|
||||
const baseUrl = selectedProvider?.base_url ?? "";
|
||||
|
|
@ -74,10 +76,23 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
|
|||
}, [providers]);
|
||||
|
||||
const handleProviderSelect = useCallback(
|
||||
(providerId: string) => {
|
||||
if (providerId !== selectedProviderId) {
|
||||
void setPostProcessProvider(providerId);
|
||||
async (providerId: string) => {
|
||||
// Clear error state on any selection attempt (allows dismissing the error)
|
||||
setAppleIntelligenceUnavailable(false);
|
||||
|
||||
if (providerId === selectedProviderId) return;
|
||||
|
||||
// Check Apple Intelligence availability before selecting
|
||||
if (providerId === APPLE_PROVIDER_ID) {
|
||||
const available = await commands.checkAppleIntelligenceAvailable();
|
||||
if (!available) {
|
||||
setAppleIntelligenceUnavailable(true);
|
||||
// Don't return - still set the provider so dropdown shows the selection
|
||||
// The backend gracefully handles unavailable Apple Intelligence
|
||||
}
|
||||
}
|
||||
|
||||
void setPostProcessProvider(providerId);
|
||||
},
|
||||
[selectedProviderId, setPostProcessProvider],
|
||||
);
|
||||
|
|
@ -182,6 +197,7 @@ export const usePostProcessProviderState = (): PostProcessProviderState => {
|
|||
selectedProvider,
|
||||
isCustomProvider,
|
||||
isAppleProvider,
|
||||
appleIntelligenceUnavailable,
|
||||
baseUrl,
|
||||
handleBaseUrlChange,
|
||||
isBaseUrlUpdating,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,14 @@ const PostProcessingSettingsApiComponent: React.FC = () => {
|
|||
</div>
|
||||
</SettingContainer>
|
||||
|
||||
{state.appleIntelligenceUnavailable ? (
|
||||
<div className="p-3 bg-red-500/10 border border-red-500/50">
|
||||
<p className="text-sm text-red-500">
|
||||
{t("settings.postProcessing.api.appleIntelligence.unavailable")}
|
||||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{state.isAppleProvider ? (
|
||||
<SettingContainer
|
||||
title={t("settings.postProcessing.api.appleIntelligence.title")}
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Läuft vollständig auf dem Gerät. Kein API-Schlüssel oder Netzwerkzugriff erforderlich.",
|
||||
"requirements": "Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer. Apple Intelligence muss in den Systemeinstellungen aktiviert sein."
|
||||
"requirements": "Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer. Apple Intelligence muss in den Systemeinstellungen aktiviert sein.",
|
||||
"unavailable": "Apple Intelligence ist auf diesem Gerät nicht verfügbar. Erfordert einen Apple Silicon Mac mit macOS Tahoe (26.0) oder neuer und aktiviertem Apple Intelligence in den Systemeinstellungen."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "Basis-URL",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Runs fully on-device. No API key or network access is required.",
|
||||
"requirements": "Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later. Apple Intelligence must be enabled in System Settings."
|
||||
"requirements": "Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later. Apple Intelligence must be enabled in System Settings.",
|
||||
"unavailable": "Apple Intelligence is not available on this device. Requires an Apple Silicon Mac running macOS Tahoe (26.0) or later with Apple Intelligence enabled in System Settings."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "Base URL",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Se ejecuta completamente en el dispositivo. No se requiere clave API ni acceso a la red.",
|
||||
"requirements": "Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior. Apple Intelligence debe estar habilitado en Ajustes del Sistema."
|
||||
"requirements": "Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior. Apple Intelligence debe estar habilitado en Ajustes del Sistema.",
|
||||
"unavailable": "Apple Intelligence no está disponible en este dispositivo. Requiere un Mac con Apple Silicon ejecutando macOS Tahoe (26.0) o posterior con Apple Intelligence habilitado en Ajustes del Sistema."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "URL Base",
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Fonctionne entièrement sur l'appareil. Aucune clé API ni accès réseau n'est requis.",
|
||||
"requirements": "Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure. Apple Intelligence doit être activé dans les Préférences Système."
|
||||
"requirements": "Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure. Apple Intelligence doit être activé dans les Préférences Système.",
|
||||
"unavailable": "Apple Intelligence n'est pas disponible sur cet appareil. Nécessite un Mac Apple Silicon exécutant macOS Tahoe (26.0) ou une version ultérieure avec Apple Intelligence activé dans les Préférences Système."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "URL de base",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Si esegue completamente in locale. Non è necessaria una chiave API né l'accesso alla rete.",
|
||||
"requirements": "Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi. Apple Intelligence deve essere abilitata nelle Impostazioni di Sistema."
|
||||
"requirements": "Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi. Apple Intelligence deve essere abilitata nelle Impostazioni di Sistema.",
|
||||
"unavailable": "Apple Intelligence non è disponibile su questo dispositivo. Necessita di un Mac con Apple Silicon e macOS Tahoe (26.0) o successivi con Apple Intelligence abilitata nelle Impostazioni di Sistema."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "URL Base",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "完全にデバイス上で動作。APIキーやネットワークアクセスは不要。",
|
||||
"requirements": "macOS Tahoe(26.0)以降を実行するApple Silicon Macが必要です。システム設定でApple Intelligenceを有効にする必要があります。"
|
||||
"requirements": "macOS Tahoe(26.0)以降を実行するApple Silicon Macが必要です。システム設定でApple Intelligenceを有効にする必要があります。",
|
||||
"unavailable": "このデバイスではApple Intelligenceを利用できません。macOS Tahoe(26.0)以降を実行し、システム設定でApple Intelligenceが有効になっているApple Silicon Macが必要です。"
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "ベースURL",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Działa całkowicie na urządzeniu. Nie wymaga klucza API ani dostępu do sieci.",
|
||||
"requirements": "Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym. Apple Intelligence musi być włączone w Ustawieniach systemowych."
|
||||
"requirements": "Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym. Apple Intelligence musi być włączone w Ustawieniach systemowych.",
|
||||
"unavailable": "Apple Intelligence nie jest dostępne na tym urządzeniu. Wymaga komputera Mac z Apple Silicon i systemem macOS Tahoe (26.0) lub nowszym z włączonym Apple Intelligence w Ustawieniach systemowych."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "Adres bazowy",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Интеллект",
|
||||
"description": "Полностью работает на устройстве. Никакой ключ API или доступ к сети не требуется.",
|
||||
"requirements": "Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии. Apple Intelligence должен быть включен в настройках системы."
|
||||
"requirements": "Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии. Apple Intelligence должен быть включен в настройках системы.",
|
||||
"unavailable": "Apple Intelligence недоступен на этом устройстве. Требуется Apple Silicon Mac под управлением macOS Tahoe (26.0) или более поздней версии с включенным Apple Intelligence в настройках системы."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "Базовый URL",
|
||||
|
|
|
|||
|
|
@ -216,7 +216,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "Chạy hoàn toàn trên thiết bị. Không cần khóa API hoặc truy cập mạng.",
|
||||
"requirements": "Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên. Apple Intelligence phải được bật trong Cài đặt Hệ thống."
|
||||
"requirements": "Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên. Apple Intelligence phải được bật trong Cài đặt Hệ thống.",
|
||||
"unavailable": "Apple Intelligence không khả dụng trên thiết bị này. Yêu cầu Mac Apple Silicon chạy macOS Tahoe (26.0) trở lên với Apple Intelligence được bật trong Cài đặt Hệ thống."
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "URL cơ sở",
|
||||
|
|
|
|||
|
|
@ -215,7 +215,8 @@
|
|||
"appleIntelligence": {
|
||||
"title": "Apple Intelligence",
|
||||
"description": "完全在设备上运行。无需 API 密钥或网络访问。",
|
||||
"requirements": "需要运行 macOS Tahoe(26.0)或更高版本的 Apple Silicon Mac。必须在系统设置中启用 Apple Intelligence。"
|
||||
"requirements": "需要运行 macOS Tahoe(26.0)或更高版本的 Apple Silicon Mac。必须在系统设置中启用 Apple Intelligence。",
|
||||
"unavailable": "Apple Intelligence 在此设备上不可用。需要运行 macOS Tahoe(26.0)或更高版本的 Apple Silicon Mac,并在系统设置中启用 Apple Intelligence。"
|
||||
},
|
||||
"baseUrl": {
|
||||
"title": "基础 URL",
|
||||
|
|
|
|||
Loading…
Reference in a new issue