diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 662d42c4f1c..8ca6d1ce62e 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -220,7 +220,7 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "percent-encoding", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "x11rb", ] @@ -1678,7 +1678,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1989,7 +1989,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3974,7 +3974,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4108,6 +4108,17 @@ dependencies = [ "objc2-foundation 0.3.2", ] +[[package]] +name = "objc2-contacts" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b034b578389f89a85c055eacc8d8b368be5f04a6c1b07f672bf3aec21d0ef621" +dependencies = [ + "block2 0.6.2", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -4466,6 +4477,7 @@ dependencies = [ "async-trait", "axum", "base64 0.22.1", + "block2 0.6.2", "chacha20poly1305", "chrono", "chrono-tz", @@ -4483,6 +4495,7 @@ dependencies = [ "fs2", "futures", "futures-util", + "glob", "hex", "hmac 0.12.1", "hostname", @@ -4493,6 +4506,9 @@ dependencies = [ "log", "mail-parser", "nu-ansi-term 0.46.0", + "objc2 0.6.4", + "objc2-contacts", + "objc2-foundation 0.3.2", "once_cell", "opentelemetry", "opentelemetry-otlp", @@ -4538,6 +4554,7 @@ dependencies = [ "urlencoding", "uuid", "wait-timeout", + "walkdir", "webpki-roots 1.0.6", "whisper-rs", "xz2", @@ -5420,7 +5437,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5846,7 +5863,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5905,7 +5922,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -7236,7 +7253,7 @@ dependencies = [ "getrandom 0.4.2", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -8534,7 +8551,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/app/src/components/settings/panels/LocalModelPanel.tsx b/app/src/components/settings/panels/LocalModelPanel.tsx index 2d47e52e487..3a0b7b6d0cf 100644 --- a/app/src/components/settings/panels/LocalModelPanel.tsx +++ b/app/src/components/settings/panels/LocalModelPanel.tsx @@ -10,11 +10,13 @@ import { type ApplyPresetResult, type LocalAiDownloadsProgress, type LocalAiStatus, + openhumanGetConfig, openhumanLocalAiDownload, openhumanLocalAiDownloadAllAssets, openhumanLocalAiDownloadsProgress, openhumanLocalAiPresets, openhumanLocalAiStatus, + openhumanUpdateLocalAiSettings, type PresetsResponse, } from '../../../utils/tauriCommands'; import SettingsHeader from '../components/SettingsHeader'; @@ -39,6 +41,22 @@ const LocalModelPanel = () => { const [presetError, setPresetError] = useState(''); const [presetSuccess, setPresetSuccess] = useState(null); + const [usageFlags, setUsageFlags] = useState<{ + runtime_enabled: boolean; + usage_embeddings: boolean; + usage_heartbeat: boolean; + usage_learning_reflection: boolean; + usage_subconscious: boolean; + }>({ + runtime_enabled: false, + usage_embeddings: false, + usage_heartbeat: false, + usage_learning_reflection: false, + usage_subconscious: false, + }); + const [usageError, setUsageError] = useState(''); + const [usageSaving, setUsageSaving] = useState(false); + const progress = useMemo(() => { const downloadProgress = progressFromDownloads(downloads); if (downloadProgress != null) return downloadProgress; @@ -87,9 +105,45 @@ const LocalModelPanel = () => { } }; + const loadUsage = async () => { + try { + const snap = await openhumanGetConfig(); + const localAi = (snap.result?.config?.local_ai ?? {}) as Record; + const usage = (localAi.usage ?? {}) as Record; + setUsageFlags({ + runtime_enabled: Boolean(localAi.runtime_enabled), + usage_embeddings: Boolean(usage.embeddings), + usage_heartbeat: Boolean(usage.heartbeat), + usage_learning_reflection: Boolean(usage.learning_reflection), + usage_subconscious: Boolean(usage.subconscious), + }); + setUsageError(''); + } catch (err) { + const msg = err instanceof Error ? err.message : 'Failed to load local AI flags'; + setUsageError(msg); + } + }; + + const updateUsage = async (patch: Partial) => { + const next = { ...usageFlags, ...patch }; + setUsageFlags(next); + setUsageSaving(true); + setUsageError(''); + try { + await openhumanUpdateLocalAiSettings(patch); + } catch (err) { + const msg = err instanceof Error ? err.message : 'Failed to save local AI flags'; + setUsageError(msg); + void loadUsage(); + } finally { + setUsageSaving(false); + } + }; + useEffect(() => { void loadStatus(); void loadPresets(); + void loadUsage(); const timer = setInterval(() => { void loadStatus(); }, 1500); @@ -212,6 +266,79 @@ const LocalModelPanel = () => { )} +
+
+

Usage

+

+ Choose which subsystems run on the local model. Anything off uses the cloud. +

+
+ + + +
+ {( + [ + { + key: 'usage_embeddings' as const, + label: 'Embeddings', + hint: 'Generate memory embeddings locally instead of in the cloud.', + }, + { + key: 'usage_heartbeat' as const, + label: 'Heartbeat', + hint: 'Run heartbeat reasoning locally.', + }, + { + key: 'usage_learning_reflection' as const, + label: 'Learning / reflection', + hint: 'Run learning and reflection passes locally.', + }, + { + key: 'usage_subconscious' as const, + label: 'Subconscious', + hint: 'Run subconscious evaluation locally.', + }, + ] as const + ).map(({ key, label, hint }) => ( + + ))} +
+ + {usageError && ( +
+ {usageError} +
+ )} +
+