agent: lumotia-rebrand — rust workspace crates magnotia-* -> lumotia-*
Phase 2 of the rebrand cascade. Renames all 9 workspace crates from magnotia-* to lumotia-* plus the src-tauri binary crate name: - magnotia-ai-formatting -> lumotia-ai-formatting - magnotia-audio -> lumotia-audio - magnotia-cloud-providers -> lumotia-cloud-providers - magnotia-core -> lumotia-core - magnotia-hotkey -> lumotia-hotkey - magnotia-llm -> lumotia-llm - magnotia-mcp -> lumotia-mcp - magnotia-storage -> lumotia-storage - magnotia-transcription -> lumotia-transcription - magnotia -> lumotia (src-tauri binary) - magnotia_lib -> lumotia_lib (src-tauri lib target) Crate directories (crates/audio/ etc.) stay as-is; only the Cargo.toml [package] name field changes plus all consumer module imports (magnotia_core -> lumotia_core, etc.). Remaining magnotia_* references at this point are intentional and scoped to later phases: tracing targets (Phase 4), DB setting keys magnotia_preferences/magnotia_history (Phase 5). cargo build --workspace passes. cargo test --workspace: 330 pass, 0 fail. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
326
Cargo.lock
generated
326
Cargo.lock
generated
@@ -2752,6 +2752,169 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"arboard",
|
||||||
|
"base64 0.22.1",
|
||||||
|
"gdk",
|
||||||
|
"gtk",
|
||||||
|
"lumotia-ai-formatting",
|
||||||
|
"lumotia-audio",
|
||||||
|
"lumotia-cloud-providers",
|
||||||
|
"lumotia-core",
|
||||||
|
"lumotia-hotkey",
|
||||||
|
"lumotia-llm",
|
||||||
|
"lumotia-storage",
|
||||||
|
"lumotia-transcription",
|
||||||
|
"objc2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sqlx",
|
||||||
|
"tauri",
|
||||||
|
"tauri-build",
|
||||||
|
"tauri-plugin-autostart",
|
||||||
|
"tauri-plugin-dialog",
|
||||||
|
"tauri-plugin-global-shortcut",
|
||||||
|
"tauri-plugin-notification",
|
||||||
|
"tauri-plugin-opener",
|
||||||
|
"tauri-plugin-window-state",
|
||||||
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"uuid",
|
||||||
|
"webkit2gtk",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-ai-formatting"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"lumotia-core",
|
||||||
|
"lumotia-llm",
|
||||||
|
"regex-lite",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-audio"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cpal",
|
||||||
|
"hound",
|
||||||
|
"lumotia-core",
|
||||||
|
"regex",
|
||||||
|
"rubato",
|
||||||
|
"serde",
|
||||||
|
"symphonia",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-cloud-providers"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"lumotia-core",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"libloading 0.8.9",
|
||||||
|
"num_cpus",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sysinfo",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-hotkey"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"evdev",
|
||||||
|
"lumotia-core",
|
||||||
|
"nix 0.29.0",
|
||||||
|
"notify",
|
||||||
|
"serde",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-llm"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_rs",
|
||||||
|
"futures-util",
|
||||||
|
"llama-cpp-2",
|
||||||
|
"lumotia-core",
|
||||||
|
"reqwest 0.12.28",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-mcp"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"lumotia-storage",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sqlx",
|
||||||
|
"tempfile",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-storage"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"lumotia-core",
|
||||||
|
"serde",
|
||||||
|
"sqlx",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lumotia-transcription"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"futures-util",
|
||||||
|
"lumotia-cloud-providers",
|
||||||
|
"lumotia-core",
|
||||||
|
"num_cpus",
|
||||||
|
"reqwest 0.12.28",
|
||||||
|
"sha2",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
"transcribe-rs",
|
||||||
|
"whisper-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lzma-rust2"
|
name = "lzma-rust2"
|
||||||
version = "0.15.7"
|
version = "0.15.7"
|
||||||
@@ -2785,169 +2948,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"arboard",
|
|
||||||
"base64 0.22.1",
|
|
||||||
"gdk",
|
|
||||||
"gtk",
|
|
||||||
"magnotia-ai-formatting",
|
|
||||||
"magnotia-audio",
|
|
||||||
"magnotia-cloud-providers",
|
|
||||||
"magnotia-core",
|
|
||||||
"magnotia-hotkey",
|
|
||||||
"magnotia-llm",
|
|
||||||
"magnotia-storage",
|
|
||||||
"magnotia-transcription",
|
|
||||||
"objc2",
|
|
||||||
"objc2-foundation",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sqlx",
|
|
||||||
"tauri",
|
|
||||||
"tauri-build",
|
|
||||||
"tauri-plugin-autostart",
|
|
||||||
"tauri-plugin-dialog",
|
|
||||||
"tauri-plugin-global-shortcut",
|
|
||||||
"tauri-plugin-notification",
|
|
||||||
"tauri-plugin-opener",
|
|
||||||
"tauri-plugin-window-state",
|
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"uuid",
|
|
||||||
"webkit2gtk",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-ai-formatting"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"magnotia-core",
|
|
||||||
"magnotia-llm",
|
|
||||||
"regex-lite",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-audio"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"cpal",
|
|
||||||
"hound",
|
|
||||||
"magnotia-core",
|
|
||||||
"regex",
|
|
||||||
"rubato",
|
|
||||||
"serde",
|
|
||||||
"symphonia",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-cloud-providers"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"magnotia-core",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-core"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"libloading 0.8.9",
|
|
||||||
"num_cpus",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sysinfo",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-hotkey"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"evdev",
|
|
||||||
"magnotia-core",
|
|
||||||
"nix 0.29.0",
|
|
||||||
"notify",
|
|
||||||
"serde",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-llm"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"encoding_rs",
|
|
||||||
"futures-util",
|
|
||||||
"llama-cpp-2",
|
|
||||||
"magnotia-core",
|
|
||||||
"reqwest 0.12.28",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sha2",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-mcp"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"magnotia-storage",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sqlx",
|
|
||||||
"tempfile",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-storage"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"magnotia-core",
|
|
||||||
"serde",
|
|
||||||
"sqlx",
|
|
||||||
"thiserror 1.0.69",
|
|
||||||
"tokio",
|
|
||||||
"uuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "magnotia-transcription"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"futures-util",
|
|
||||||
"magnotia-cloud-providers",
|
|
||||||
"magnotia-core",
|
|
||||||
"num_cpus",
|
|
||||||
"reqwest 0.12.28",
|
|
||||||
"sha2",
|
|
||||||
"tempfile",
|
|
||||||
"thiserror 2.0.18",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"transcribe-rs",
|
|
||||||
"whisper-rs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markup5ever"
|
name = "markup5ever"
|
||||||
version = "0.14.1"
|
version = "0.14.1"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-ai-formatting"
|
name = "lumotia-ai-formatting"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Text post-processing pipeline: filler removal, British English conversion, formatting for Magnotia"
|
description = "Text post-processing pipeline: filler removal, British English conversion, formatting for Magnotia"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
magnotia-llm = { path = "../llm" }
|
lumotia-llm = { path = "../llm" }
|
||||||
regex-lite = "0.1"
|
regex-lite = "0.1"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//! The llm_client is not yet wired to a running model. This module defines
|
//! The llm_client is not yet wired to a running model. This module defines
|
||||||
//! the prompt contract so that wiring it produces correct, hardened output.
|
//! the prompt contract so that wiring it produces correct, hardened output.
|
||||||
|
|
||||||
use magnotia_llm::{EngineError, LlmEngine};
|
use lumotia_llm::{EngineError, LlmEngine};
|
||||||
|
|
||||||
/// System prompt sent before every cleanup call.
|
/// System prompt sent before every cleanup call.
|
||||||
///
|
///
|
||||||
@@ -161,7 +161,7 @@ pub fn cleanup_text(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use magnotia_llm::EngineError;
|
use lumotia_llm::EngineError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_terms_returns_empty_string() {
|
fn empty_terms_returns_empty_string() {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use magnotia_core::constants::SMART_PARAGRAPH_GAP_SECS;
|
use lumotia_core::constants::SMART_PARAGRAPH_GAP_SECS;
|
||||||
use magnotia_core::types::Segment;
|
use lumotia_core::types::Segment;
|
||||||
use magnotia_llm::LlmEngine;
|
use lumotia_llm::LlmEngine;
|
||||||
|
|
||||||
use crate::{llm_client, rule_based, to_plain_text::to_plain_text};
|
use crate::{llm_client, rule_based, to_plain_text::to_plain_text};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
//! is the whitespace pass and empty-segment filter, plus a single
|
//! is the whitespace pass and empty-segment filter, plus a single
|
||||||
//! public function the pipeline can depend on.
|
//! public function the pipeline can depend on.
|
||||||
|
|
||||||
use magnotia_core::types::Segment;
|
use lumotia_core::types::Segment;
|
||||||
|
|
||||||
/// Join transcription segments into a single plain-text string
|
/// Join transcription segments into a single plain-text string
|
||||||
/// suitable for feeding to an LLM cleanup prompt.
|
/// suitable for feeding to an LLM cleanup prompt.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-audio"
|
name = "lumotia-audio"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Audio capture (cpal), VAD, resampling (rubato), file decoding (symphonia), WAV I/O (hound) for Magnotia"
|
description = "Audio capture (cpal), VAD, resampling (rubato), file decoding (symphonia), WAV I/O (hound) for Magnotia"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
|
|
||||||
# Microphone capture
|
# Microphone capture
|
||||||
cpal = "0.17"
|
cpal = "0.17"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use regex::Regex;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
|
|
||||||
const AUDIO_CHANNEL_CAPACITY: usize = 32;
|
const AUDIO_CHANNEL_CAPACITY: usize = 32;
|
||||||
|
|
||||||
@@ -149,7 +149,7 @@ impl MicrophoneCapture {
|
|||||||
for device in devices {
|
for device in devices {
|
||||||
let name = device_display_name(&device).unwrap_or_default();
|
let name = device_display_name(&device).unwrap_or_default();
|
||||||
if name == device_name {
|
if name == device_name {
|
||||||
tracing::info!(target: "magnotia_audio", "start_with_device: opening explicit device '{name}'");
|
tracing::info!(target: "lumotia_audio", "start_with_device: opening explicit device '{name}'");
|
||||||
return open_and_validate(device, &name, /* require_audio = */ true);
|
return open_and_validate(device, &name, /* require_audio = */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,7 +196,7 @@ impl MicrophoneCapture {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
device_count = all_devices.len(),
|
device_count = all_devices.len(),
|
||||||
default = %default_name,
|
default = %default_name,
|
||||||
"enumerated input devices"
|
"enumerated input devices"
|
||||||
@@ -211,7 +211,7 @@ impl MicrophoneCapture {
|
|||||||
match open_and_validate(device.clone(), &name, true) {
|
match open_and_validate(device.clone(), &name, true) {
|
||||||
Ok(result) => return Ok(result),
|
Ok(result) => return Ok(result),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(target: "magnotia_audio", device = %name, error = %e, "candidate device rejected");
|
tracing::warn!(target: "lumotia_audio", device = %name, error = %e, "candidate device rejected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@ impl MicrophoneCapture {
|
|||||||
// Second pass: accept anything that delivers bytes (monitor sources
|
// Second pass: accept anything that delivers bytes (monitor sources
|
||||||
// included). Better to capture from a monitor than fail entirely.
|
// included). Better to capture from a monitor than fail entirely.
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
"no non-monitor mic produced audio; falling back to monitor/loopback sources"
|
"no non-monitor mic produced audio; falling back to monitor/loopback sources"
|
||||||
);
|
);
|
||||||
for device in &all_devices {
|
for device in &all_devices {
|
||||||
@@ -227,7 +227,7 @@ impl MicrophoneCapture {
|
|||||||
match open_and_validate(device.clone(), &name, false) {
|
match open_and_validate(device.clone(), &name, false) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
device = %name,
|
device = %name,
|
||||||
"capturing from likely monitor source; recordings may be silent or contain system audio"
|
"capturing from likely monitor source; recordings may be silent or contain system audio"
|
||||||
);
|
);
|
||||||
@@ -367,7 +367,7 @@ fn open_and_validate(
|
|||||||
let format = config.sample_format();
|
let format = config.sample_format();
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
device = %name,
|
device = %name,
|
||||||
sample_rate,
|
sample_rate,
|
||||||
channels,
|
channels,
|
||||||
@@ -467,7 +467,7 @@ fn open_and_validate(
|
|||||||
|
|
||||||
let rms = (sum_sq / total_samples as f64).sqrt() as f32;
|
let rms = (sum_sq / total_samples as f64).sqrt() as f32;
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
device = %name,
|
device = %name,
|
||||||
samples = total_samples,
|
samples = total_samples,
|
||||||
rms,
|
rms,
|
||||||
@@ -499,7 +499,7 @@ fn open_and_validate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!(target: "magnotia_audio", device = %name, "selected microphone");
|
tracing::info!(target: "lumotia_audio", device = %name, "selected microphone");
|
||||||
Ok((
|
Ok((
|
||||||
MicrophoneCapture {
|
MicrophoneCapture {
|
||||||
stream: Some(stream),
|
stream: Some(stream),
|
||||||
@@ -547,7 +547,7 @@ where
|
|||||||
move |err| {
|
move |err| {
|
||||||
// Surface stream errors to the live session via err_tx so the
|
// Surface stream errors to the live session via err_tx so the
|
||||||
// frontend can show a toast.
|
// frontend can show a toast.
|
||||||
tracing::error!(target: "magnotia_audio", error = %err, "capture stream error");
|
tracing::error!(target: "lumotia_audio", error = %err, "capture stream error");
|
||||||
if err_tx
|
if err_tx
|
||||||
.try_send(CaptureRuntimeError {
|
.try_send(CaptureRuntimeError {
|
||||||
device_name: err_device_name.clone(),
|
device_name: err_device_name.clone(),
|
||||||
@@ -560,7 +560,7 @@ where
|
|||||||
// even if the frontend never received the typed event.
|
// even if the frontend never received the typed event.
|
||||||
let prior = dropped_errors.fetch_add(1, Ordering::Relaxed);
|
let prior = dropped_errors.fetch_add(1, Ordering::Relaxed);
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "magnotia_audio",
|
target: "lumotia_audio",
|
||||||
device = %err_device_name,
|
device = %err_device_name,
|
||||||
dropped_error = prior + 1,
|
dropped_error = prior + 1,
|
||||||
"capture error channel full; dropping runtime error"
|
"capture error channel full; dropping runtime error"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use magnotia_core::error::Result;
|
use lumotia_core::error::Result;
|
||||||
use magnotia_core::types::AudioSamples;
|
use lumotia_core::types::AudioSamples;
|
||||||
|
|
||||||
use crate::decode::decode_audio_file;
|
use crate::decode::decode_audio_file;
|
||||||
use crate::resample::resample_to_16khz;
|
use crate::resample::resample_to_16khz;
|
||||||
@@ -16,6 +16,6 @@ pub async fn decode_and_resample(path: &Path) -> Result<AudioSamples> {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
magnotia_core::error::MagnotiaError::AudioDecodeFailed(format!("Task join error: {e}"))
|
lumotia_core::error::MagnotiaError::AudioDecodeFailed(format!("Task join error: {e}"))
|
||||||
})?
|
})?
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ use symphonia::core::io::MediaSourceStream;
|
|||||||
use symphonia::core::meta::MetadataOptions;
|
use symphonia::core::meta::MetadataOptions;
|
||||||
use symphonia::core::probe::Hint;
|
use symphonia::core::probe::Hint;
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::AudioSamples;
|
use lumotia_core::types::AudioSamples;
|
||||||
|
|
||||||
/// Decode an audio file to mono f32 PCM samples.
|
/// Decode an audio file to mono f32 PCM samples.
|
||||||
/// Supports all formats symphonia handles: mp3, aac, flac, wav, ogg, etc.
|
/// Supports all formats symphonia handles: mp3, aac, flac, wav, ogg, etc.
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use rubato::{
|
|||||||
Resampler, SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
|
Resampler, SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use magnotia_core::constants::WHISPER_SAMPLE_RATE;
|
use lumotia_core::constants::WHISPER_SAMPLE_RATE;
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::AudioSamples;
|
use lumotia_core::types::AudioSamples;
|
||||||
|
|
||||||
/// Resample audio to 16kHz mono using sinc interpolation (rubato).
|
/// Resample audio to 16kHz mono using sinc interpolation (rubato).
|
||||||
/// Returns a new AudioSamples at the target sample rate.
|
/// Returns a new AudioSamples at the target sample rate.
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ use rubato::{
|
|||||||
Resampler, SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
|
Resampler, SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
|
||||||
};
|
};
|
||||||
|
|
||||||
use magnotia_core::constants::WHISPER_SAMPLE_RATE;
|
use lumotia_core::constants::WHISPER_SAMPLE_RATE;
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
|
|
||||||
/// Number of input samples the rubato resampler consumes per `process()`
|
/// Number of input samples the rubato resampler consumes per `process()`
|
||||||
/// call. Matches the chunk size used in `resample::resample_to_16khz`.
|
/// call. Matches the chunk size used in `resample::resample_to_16khz`.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// For now, all audio is treated as speech. This matches v0.2 behaviour
|
// For now, all audio is treated as speech. This matches v0.2 behaviour
|
||||||
// (no VAD) and doesn't affect core functionality.
|
// (no VAD) and doesn't affect core functionality.
|
||||||
|
|
||||||
use magnotia_core::constants::VAD_SPEECH_THRESHOLD;
|
use lumotia_core::constants::VAD_SPEECH_THRESHOLD;
|
||||||
|
|
||||||
/// Stub speech detector. Treats all audio as speech.
|
/// Stub speech detector. Treats all audio as speech.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::AudioSamples;
|
use lumotia_core::types::AudioSamples;
|
||||||
|
|
||||||
/// Append-friendly WAV writer for long-running captures.
|
/// Append-friendly WAV writer for long-running captures.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-cloud-providers"
|
name = "lumotia-cloud-providers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Provider trait and BYOK cloud STT scaffolding for Magnotia (Wyrdnote pending rebrand)"
|
description = "Provider trait and BYOK cloud STT scaffolding for Magnotia (Wyrdnote pending rebrand)"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
|
|
||||||
# Async-native trait. async_trait converts async fn into Pin<Box<dyn
|
# Async-native trait. async_trait converts async fn into Pin<Box<dyn
|
||||||
# Future>> at the trait surface so TranscriptionProvider stays
|
# Future>> at the trait surface so TranscriptionProvider stays
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use magnotia_core::error::Result;
|
use lumotia_core::error::Result;
|
||||||
use magnotia_core::types::{AudioSamples, ModelId, Transcript, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, ModelId, Transcript, TranscriptionOptions};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// Stable, lower-kebab-case identifier for a provider. Used in user
|
/// Stable, lower-kebab-case identifier for a provider. Used in user
|
||||||
@@ -70,7 +70,7 @@ pub enum CostClass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Capabilities a provider advertises to the orchestrator and the UI.
|
/// Capabilities a provider advertises to the orchestrator and the UI.
|
||||||
/// Superset of `magnotia_transcription::TranscriberCapabilities` for
|
/// Superset of `lumotia_transcription::TranscriberCapabilities` for
|
||||||
/// local providers, with extra fields cloud providers populate.
|
/// local providers, with extra fields cloud providers populate.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct ProviderCapabilities {
|
pub struct ProviderCapabilities {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-core"
|
name = "lumotia-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Core types, constants, traits, hardware detection, and model registry for Magnotia"
|
description = "Core types, constants, traits, hardware detection, and model registry for Magnotia"
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
//! exactly one INFO line; subsequent calls with the same tuple are
|
//! exactly one INFO line; subsequent calls with the same tuple are
|
||||||
//! silenced by the per-process log-once cache.
|
//! silenced by the per-process log-once cache.
|
||||||
|
|
||||||
use magnotia_core::tuning::{inference_thread_count, Workload};
|
use lumotia_core::tuning::{inference_thread_count, Workload};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
.with_env_filter("magnotia_core=info")
|
.with_env_filter("lumotia_core=info")
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
.with_writer(std::io::stderr)
|
.with_writer(std::io::stderr)
|
||||||
.init();
|
.init();
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub enum MagnotiaError {
|
|||||||
#[error("file not found: '{}'", .0.display())]
|
#[error("file not found: '{}'", .0.display())]
|
||||||
FileNotFound(PathBuf),
|
FileNotFound(PathBuf),
|
||||||
|
|
||||||
/// Structured storage failure flowed up from `magnotia_storage::Error` via
|
/// Structured storage failure flowed up from `lumotia_storage::Error` via
|
||||||
/// its `From` impl. Display reads through to `detail` so the operation +
|
/// its `From` impl. Display reads through to `detail` so the operation +
|
||||||
/// source context produced by the storage crate isn't double-prefixed.
|
/// source context produced by the storage crate isn't double-prefixed.
|
||||||
#[error("{detail}")]
|
#[error("{detail}")]
|
||||||
@@ -58,7 +58,7 @@ pub enum MagnotiaError {
|
|||||||
/// Coarse discriminator for `MagnotiaError::Storage`. The storage crate maps
|
/// Coarse discriminator for `MagnotiaError::Storage`. The storage crate maps
|
||||||
/// its full typed error onto one of these kinds at the boundary. Backend code
|
/// its full typed error onto one of these kinds at the boundary. Backend code
|
||||||
/// that wants finer-grained branching pattern-matches on
|
/// that wants finer-grained branching pattern-matches on
|
||||||
/// `magnotia_storage::Error` directly.
|
/// `lumotia_storage::Error` directly.
|
||||||
#[derive(Debug, Clone, Copy, Serialize)]
|
#[derive(Debug, Clone, Copy, Serialize)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum StorageKind {
|
pub enum StorageKind {
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ pub fn inference_thread_count(workload: Workload, gpu_offloaded: bool) -> usize
|
|||||||
if let Ok(mut seen) = log_seen().lock() {
|
if let Ok(mut seen) = log_seen().lock() {
|
||||||
if seen.insert(key) {
|
if seen.insert(key) {
|
||||||
tracing::info!(
|
tracing::info!(
|
||||||
target: "magnotia_core::tuning",
|
target: "lumotia_core::tuning",
|
||||||
threads = final_value,
|
threads = final_value,
|
||||||
workload = ?workload,
|
workload = ?workload,
|
||||||
gpu_offloaded,
|
gpu_offloaded,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-hotkey"
|
name = "lumotia-hotkey"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Wayland-compatible global hotkey listener for Magnotia — evdev backend with device hotplug"
|
description = "Wayland-compatible global hotkey listener for Magnotia — evdev backend with device hotplug"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
tokio = { version = "1", features = ["rt", "sync", "macros", "time"] }
|
tokio = { version = "1", features = ["rt", "sync", "macros", "time"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-llm"
|
name = "lumotia-llm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Local LLM engine for Magnotia (Qwen3.5 / Qwen3.6 via llama-cpp-2): transcript cleanup, task extraction, micro-step decomposition"
|
description = "Local LLM engine for Magnotia (Qwen3.5 / Qwen3.6 via llama-cpp-2): transcript cleanup, task extraction, micro-step decomposition"
|
||||||
@@ -7,7 +7,7 @@ description = "Local LLM engine for Magnotia (Qwen3.5 / Qwen3.6 via llama-cpp-2)
|
|||||||
[features]
|
[features]
|
||||||
# Default desktop build keeps the existing openmp + vulkan acceleration.
|
# Default desktop build keeps the existing openmp + vulkan acceleration.
|
||||||
# Mobile / CPU-only targets can drop one or both via:
|
# Mobile / CPU-only targets can drop one or both via:
|
||||||
# cargo build -p magnotia-llm --no-default-features
|
# cargo build -p lumotia-llm --no-default-features
|
||||||
# These are independent so an Android Vulkan build can opt into vulkan
|
# These are independent so an Android Vulkan build can opt into vulkan
|
||||||
# without openmp (the NDK ships OpenMP libs but the toolchain configuration
|
# without openmp (the NDK ships OpenMP libs but the toolchain configuration
|
||||||
# is fragile across NDK versions).
|
# is fragile across NDK versions).
|
||||||
@@ -16,7 +16,7 @@ gpu-vulkan = ["llama-cpp-2/vulkan"]
|
|||||||
openmp = ["llama-cpp-2/openmp"]
|
openmp = ["llama-cpp-2/openmp"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
llama-cpp-2 = { version = "0.1.146", default-features = false }
|
llama-cpp-2 = { version = "0.1.146", default-features = false }
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use llama_cpp_2::llama_batch::LlamaBatch;
|
|||||||
use llama_cpp_2::model::params::LlamaModelParams;
|
use llama_cpp_2::model::params::LlamaModelParams;
|
||||||
use llama_cpp_2::model::{AddBos, LlamaChatMessage, LlamaChatTemplate, LlamaModel};
|
use llama_cpp_2::model::{AddBos, LlamaChatMessage, LlamaChatTemplate, LlamaModel};
|
||||||
use llama_cpp_2::sampling::LlamaSampler;
|
use llama_cpp_2::sampling::LlamaSampler;
|
||||||
use magnotia_core::tuning::{inference_thread_count, Workload};
|
use lumotia_core::tuning::{inference_thread_count, Workload};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod grammars;
|
pub mod grammars;
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ pub fn recommend_tier(total_ram_bytes: u64, total_vram_bytes: Option<u64>) -> Ll
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn model_dir() -> PathBuf {
|
pub fn model_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().llm_models_dir()
|
lumotia_core::paths::app_paths().llm_models_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn model_path(id: LlmModelId) -> PathBuf {
|
pub fn model_path(id: LlmModelId) -> PathBuf {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use magnotia_llm::{is_valid_intent, LlmEngine, LlmModelId};
|
use lumotia_llm::{is_valid_intent, LlmEngine, LlmModelId};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extract_content_tags_returns_valid_pair() {
|
fn extract_content_tags_returns_valid_pair() {
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use magnotia_llm::LlmEngine;
|
use lumotia_llm::LlmEngine;
|
||||||
use magnotia_llm::LlmModelId;
|
use lumotia_llm::LlmModelId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn llama_cpp_2_smoke_generates_and_wraps() {
|
fn llama_cpp_2_smoke_generates_and_wraps() {
|
||||||
@@ -32,7 +32,7 @@ fn llama_cpp_2_smoke_generates_and_wraps() {
|
|||||||
let completion = engine
|
let completion = engine
|
||||||
.generate(
|
.generate(
|
||||||
"Write exactly one short greeting.",
|
"Write exactly one short greeting.",
|
||||||
&magnotia_llm::GenerationConfig {
|
&lumotia_llm::GenerationConfig {
|
||||||
max_tokens: 32,
|
max_tokens: 32,
|
||||||
temperature: 0.0,
|
temperature: 0.0,
|
||||||
stop_sequences: vec!["\n".to_string()],
|
stop_sequences: vec!["\n".to_string()],
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-mcp"
|
name = "lumotia-mcp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Read-only MCP stdio server exposing Magnotia transcripts and tasks to external agents"
|
description = "Read-only MCP stdio server exposing Magnotia transcripts and tasks to external agents"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "magnotia-mcp"
|
name = "lumotia-mcp"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-storage = { path = "../storage" }
|
lumotia-storage = { path = "../storage" }
|
||||||
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] }
|
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ async fn list_transcripts_tool(pool: &SqlitePool, args: Value) -> Result<Value,
|
|||||||
};
|
};
|
||||||
let limit = args.limit.unwrap_or(20).clamp(1, 200);
|
let limit = args.limit.unwrap_or(20).clamp(1, 200);
|
||||||
|
|
||||||
let rows = magnotia_storage::list_transcripts(pool, limit)
|
let rows = lumotia_storage::list_transcripts(pool, limit)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
||||||
|
|
||||||
@@ -239,7 +239,7 @@ async fn get_transcript_tool(pool: &SqlitePool, args: Value) -> Result<Value, Js
|
|||||||
let args: Args = serde_json::from_value(args)
|
let args: Args = serde_json::from_value(args)
|
||||||
.map_err(|e| error(-32602, format!("Invalid arguments: {e}")))?;
|
.map_err(|e| error(-32602, format!("Invalid arguments: {e}")))?;
|
||||||
|
|
||||||
let row = magnotia_storage::get_transcript(pool, &args.id)
|
let row = lumotia_storage::get_transcript(pool, &args.id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| error(-32603, format!("DB error: {e}")))?
|
.map_err(|e| error(-32603, format!("DB error: {e}")))?
|
||||||
.ok_or_else(|| error(-32000, format!("Transcript {} not found", args.id)))?;
|
.ok_or_else(|| error(-32000, format!("Transcript {} not found", args.id)))?;
|
||||||
@@ -273,7 +273,7 @@ async fn search_transcripts_tool(pool: &SqlitePool, args: Value) -> Result<Value
|
|||||||
.map_err(|e| error(-32602, format!("Invalid arguments: {e}")))?;
|
.map_err(|e| error(-32602, format!("Invalid arguments: {e}")))?;
|
||||||
let limit = args.limit.unwrap_or(20).clamp(1, 100);
|
let limit = args.limit.unwrap_or(20).clamp(1, 100);
|
||||||
|
|
||||||
let rows = magnotia_storage::search_transcripts(pool, &args.query, limit)
|
let rows = lumotia_storage::search_transcripts(pool, &args.query, limit)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
||||||
|
|
||||||
@@ -296,7 +296,7 @@ async fn search_transcripts_tool(pool: &SqlitePool, args: Value) -> Result<Value
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn list_tasks_tool(pool: &SqlitePool) -> Result<Value, JsonRpcError> {
|
async fn list_tasks_tool(pool: &SqlitePool) -> Result<Value, JsonRpcError> {
|
||||||
let rows = magnotia_storage::list_tasks(pool)
|
let rows = lumotia_storage::list_tasks(pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
.map_err(|e| error(-32603, format!("DB error: {e}")))?;
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ mod tests {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let pool = sqlx::SqlitePool::connect("sqlite::memory:").await.unwrap();
|
let pool = sqlx::SqlitePool::connect("sqlite::memory:").await.unwrap();
|
||||||
magnotia_storage::migrations::run_migrations(&pool)
|
lumotia_storage::migrations::run_migrations(&pool)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let response = handle_message(&pool, request).await.expect("has response");
|
let response = handle_message(&pool, request).await.expect("has response");
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
//! Stdio entry point for magnotia-mcp. Reads newline-delimited JSON-RPC messages
|
//! Stdio entry point for magnotia-mcp. Reads newline-delimited JSON-RPC messages
|
||||||
//! from stdin, dispatches via `magnotia_mcp::handle_message`, writes responses to
|
//! from stdin, dispatches via `lumotia_mcp::handle_message`, writes responses to
|
||||||
//! stdout. Logs land on stderr so they don't collide with the JSON-RPC stream.
|
//! stdout. Logs land on stderr so they don't collide with the JSON-RPC stream.
|
||||||
|
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
|
|
||||||
#[tokio::main(flavor = "current_thread")]
|
#[tokio::main(flavor = "current_thread")]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let db_path = magnotia_storage::database_path();
|
let db_path = lumotia_storage::database_path();
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[magnotia-mcp] opening Magnotia database at {} (read-only)",
|
"[magnotia-mcp] opening Magnotia database at {} (read-only)",
|
||||||
db_path.display()
|
db_path.display()
|
||||||
@@ -15,7 +15,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
// to the user's database, regardless of which tools the dispatcher
|
// to the user's database, regardless of which tools the dispatcher
|
||||||
// exposes. Migrations are deliberately skipped — this binary never owns
|
// exposes. Migrations are deliberately skipped — this binary never owns
|
||||||
// the schema; the main app is the single migration writer.
|
// the schema; the main app is the single migration writer.
|
||||||
let pool = magnotia_storage::init_readonly(&db_path).await?;
|
let pool = lumotia_storage::init_readonly(&db_path).await?;
|
||||||
eprintln!("[magnotia-mcp] ready, waiting for JSON-RPC on stdin");
|
eprintln!("[magnotia-mcp] ready, waiting for JSON-RPC on stdin");
|
||||||
|
|
||||||
let mut lines = BufReader::new(tokio::io::stdin()).lines();
|
let mut lines = BufReader::new(tokio::io::stdin()).lines();
|
||||||
@@ -28,7 +28,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let response = match serde_json::from_str::<serde_json::Value>(trimmed) {
|
let response = match serde_json::from_str::<serde_json::Value>(trimmed) {
|
||||||
Ok(raw) => match magnotia_mcp::handle_message(&pool, raw).await {
|
Ok(raw) => match lumotia_mcp::handle_message(&pool, raw).await {
|
||||||
Some(response) => response,
|
Some(response) => response,
|
||||||
None => continue, // notification — no reply
|
None => continue, // notification — no reply
|
||||||
},
|
},
|
||||||
@@ -39,7 +39,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
// clients saw silence instead of a structured error
|
// clients saw silence instead of a structured error
|
||||||
// (2026-04-22 review MAJOR).
|
// (2026-04-22 review MAJOR).
|
||||||
eprintln!("[magnotia-mcp] parse error: {err}");
|
eprintln!("[magnotia-mcp] parse error: {err}");
|
||||||
magnotia_mcp::parse_error_response(&err.to_string())
|
lumotia_mcp::parse_error_response(&err.to_string())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-storage"
|
name = "lumotia-storage"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "SQLite persistence, BM25 search, and file storage for Magnotia"
|
description = "SQLite persistence, BM25 search, and file storage for Magnotia"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
|
|
||||||
# SQLite with compile-time checked queries
|
# SQLite with compile-time checked queries
|
||||||
# default-features = false strips sqlx's `any`, `macros`, `migrate`, `json` —
|
# default-features = false strips sqlx's `any`, `macros`, `migrate`, `json` —
|
||||||
@@ -24,7 +24,7 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
# Logging
|
# Logging
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
# Structured error derivation for magnotia_storage::Error.
|
# Structured error derivation for lumotia_storage::Error.
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
||||||
# UUIDs for profile + profile_terms ids (v7 random).
|
# UUIDs for profile + profile_terms ids (v7 random).
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Storage-local typed error.
|
//! Storage-local typed error.
|
||||||
//!
|
//!
|
||||||
//! Backend code that wants to branch on storage failure modes pattern-matches
|
//! Backend code that wants to branch on storage failure modes pattern-matches
|
||||||
//! on [`Error`] directly. The crate boundary into [`magnotia_core::error::MagnotiaError`]
|
//! on [`Error`] directly. The crate boundary into [`lumotia_core::error::MagnotiaError`]
|
||||||
//! flattens this into a [`MagnotiaError::Storage`] variant carrying a serde-friendly
|
//! flattens this into a [`MagnotiaError::Storage`] variant carrying a serde-friendly
|
||||||
//! `kind`, an `operation` label, and the Display output as `detail` — so the
|
//! `kind`, an `operation` label, and the Display output as `detail` — so the
|
||||||
//! frontend (which still receives stringified errors from Tauri commands today)
|
//! frontend (which still receives stringified errors from Tauri commands today)
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, StorageKind};
|
use lumotia_core::error::{MagnotiaError, StorageKind};
|
||||||
|
|
||||||
/// Kinds of database-open operation that can fail before the pool is ready.
|
/// Kinds of database-open operation that can fail before the pool is ready.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn app_data_dir() -> PathBuf {
|
pub fn app_data_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().app_data_dir()
|
lumotia_core::paths::app_paths().app_data_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Path to the SQLite database file.
|
/// Path to the SQLite database file.
|
||||||
pub fn database_path() -> PathBuf {
|
pub fn database_path() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().database_path()
|
lumotia_core::paths::app_paths().database_path()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Directory for saved audio recordings.
|
/// Directory for saved audio recordings.
|
||||||
pub fn recordings_dir() -> PathBuf {
|
pub fn recordings_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().recordings_dir()
|
lumotia_core::paths::app_paths().recordings_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Directory for crash dumps written by the Rust panic hook.
|
/// Directory for crash dumps written by the Rust panic hook.
|
||||||
/// Each crash is a single text file: `<unix-ts>-<short-id>.crash`.
|
/// Each crash is a single text file: `<unix-ts>-<short-id>.crash`.
|
||||||
/// Used by the diagnostic-report bundler in Settings → About.
|
/// Used by the diagnostic-report bundler in Settings → About.
|
||||||
pub fn crashes_dir() -> PathBuf {
|
pub fn crashes_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().crashes_dir()
|
lumotia_core::paths::app_paths().crashes_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Directory for the rolling Rust log file (magnotia.log + rotated magnotia.log.1, etc).
|
/// Directory for the rolling Rust log file (magnotia.log + rotated magnotia.log.1, etc).
|
||||||
/// Subscribers configured in src-tauri/src/lib.rs at startup.
|
/// Subscribers configured in src-tauri/src/lib.rs at startup.
|
||||||
pub fn logs_dir() -> PathBuf {
|
pub fn logs_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().logs_dir()
|
lumotia_core::paths::app_paths().logs_dir()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia-transcription"
|
name = "lumotia-transcription"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Speech-to-text engine wrappers, model management, and inference concurrency for Magnotia"
|
description = "Speech-to-text engine wrappers, model management, and inference concurrency for Magnotia"
|
||||||
@@ -15,18 +15,18 @@ build = "build.rs"
|
|||||||
# `whisper-vulkan` is a separate feature so a non-Vulkan target (Android
|
# `whisper-vulkan` is a separate feature so a non-Vulkan target (Android
|
||||||
# without GPU drivers, a CPU-only Windows build) can pull in whisper-rs
|
# without GPU drivers, a CPU-only Windows build) can pull in whisper-rs
|
||||||
# but skip the Vulkan backend. Build CPU-only with:
|
# but skip the Vulkan backend. Build CPU-only with:
|
||||||
# cargo build -p magnotia-transcription --no-default-features --features whisper
|
# cargo build -p lumotia-transcription --no-default-features --features whisper
|
||||||
default = ["whisper", "whisper-vulkan"]
|
default = ["whisper", "whisper-vulkan"]
|
||||||
whisper = ["dep:whisper-rs"]
|
whisper = ["dep:whisper-rs"]
|
||||||
whisper-vulkan = ["whisper-rs?/vulkan"]
|
whisper-vulkan = ["whisper-rs?/vulkan"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnotia-core = { path = "../core" }
|
lumotia-core = { path = "../core" }
|
||||||
|
|
||||||
# TranscriptionProvider async trait + EngineProfile + ProviderId. The
|
# TranscriptionProvider async trait + EngineProfile + ProviderId. The
|
||||||
# trait lives in cloud-providers so an OEM licensee can implement it
|
# trait lives in cloud-providers so an OEM licensee can implement it
|
||||||
# without depending on transcription internals.
|
# without depending on transcription internals.
|
||||||
magnotia-cloud-providers = { path = "../cloud-providers" }
|
lumotia-cloud-providers = { path = "../cloud-providers" }
|
||||||
|
|
||||||
# Async-trait for the LocalProviderAdapter impl.
|
# Async-trait for the LocalProviderAdapter impl.
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
@@ -57,12 +57,12 @@ thiserror = "2"
|
|||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# TcpListener fixture for the download resume tests (mirrors magnotia-llm).
|
# TcpListener fixture for the download resume tests (mirrors lumotia-llm).
|
||||||
# `macros` and `rt-multi-thread` enable #[tokio::test] and the multi-threaded
|
# `macros` and `rt-multi-thread` enable #[tokio::test] and the multi-threaded
|
||||||
# scheduler used by the orchestrator tests.
|
# scheduler used by the orchestrator tests.
|
||||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "net", "io-util", "macros"] }
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "net", "io-util", "macros"] }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
# Test-only — used by tests/thread_sweep.rs to label physical vs logical
|
# Test-only — used by tests/thread_sweep.rs to label physical vs logical
|
||||||
# core counts in the scaling table. Production code uses the
|
# core counts in the scaling table. Production code uses the
|
||||||
# `magnotia_core::constants::inference_thread_count` helper instead.
|
# `lumotia_core::constants::inference_thread_count` helper instead.
|
||||||
num_cpus = "1"
|
num_cpus = "1"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
//! workspace ever pulls `tokenizers` into the dependency graph on a
|
//! workspace ever pulls `tokenizers` into the dependency graph on a
|
||||||
//! Windows target. If we ever legitimately need it we can reintroduce
|
//! Windows target. If we ever legitimately need it we can reintroduce
|
||||||
//! it via a sidecar (isolated process, separate CRT) rather than
|
//! it via a sidecar (isolated process, separate CRT) rather than
|
||||||
//! linking it into `magnotia_lib`.
|
//! linking it into `lumotia_lib`.
|
||||||
//!
|
//!
|
||||||
//! The check is advisory on non-Windows targets — it still prints a
|
//! The check is advisory on non-Windows targets — it still prints a
|
||||||
//! cargo:warning if `tokenizers` appears, so the Windows failure isn't
|
//! cargo:warning if `tokenizers` appears, so the Windows failure isn't
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::{AudioSamples, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, TranscriptionOptions};
|
||||||
|
|
||||||
use crate::local_engine::{LocalEngine, TimedTranscript};
|
use crate::local_engine::{LocalEngine, TimedTranscript};
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,11 @@ pub use transcribe_rs::SpeechModel;
|
|||||||
pub use transcriber::{Transcriber, TranscriberCapabilities};
|
pub use transcriber::{Transcriber, TranscriberCapabilities};
|
||||||
|
|
||||||
// Re-export the trait surface so downstream crates depend only on
|
// Re-export the trait surface so downstream crates depend only on
|
||||||
// `magnotia_transcription` to access both local engines and the
|
// `lumotia_transcription` to access both local engines and the
|
||||||
// provider trait without a separate `magnotia_cloud_providers`
|
// provider trait without a separate `lumotia_cloud_providers`
|
||||||
// dependency. This is the seam an OEM licensee uses when they swap
|
// dependency. This is the seam an OEM licensee uses when they swap
|
||||||
// providers.
|
// providers.
|
||||||
pub use magnotia_cloud_providers::{
|
pub use lumotia_cloud_providers::{
|
||||||
CostClass, EngineProfile, NetworkRequirement, ProviderCapabilities, ProviderId, ProviderKind,
|
CostClass, EngineProfile, NetworkRequirement, ProviderCapabilities, ProviderId, ProviderKind,
|
||||||
ProviderTranscript, TranscriptionProvider,
|
ProviderTranscript, TranscriptionProvider,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use std::time::Instant;
|
|||||||
|
|
||||||
use transcribe_rs::{SpeechModel, TranscribeOptions, TranscriptionResult};
|
use transcribe_rs::{SpeechModel, TranscribeOptions, TranscriptionResult};
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::{
|
use lumotia_core::types::{
|
||||||
AudioSamples, EngineName, ModelId, Segment, Transcript, TranscriptionOptions,
|
AudioSamples, EngineName, ModelId, Segment, Transcript, TranscriptionOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ pub struct SpeechModelAdapter(pub Box<dyn SpeechModel + Send>);
|
|||||||
impl Transcriber for SpeechModelAdapter {
|
impl Transcriber for SpeechModelAdapter {
|
||||||
fn capabilities(&self) -> TranscriberCapabilities {
|
fn capabilities(&self) -> TranscriberCapabilities {
|
||||||
TranscriberCapabilities {
|
TranscriberCapabilities {
|
||||||
sample_rate: magnotia_core::constants::WHISPER_SAMPLE_RATE,
|
sample_rate: lumotia_core::constants::WHISPER_SAMPLE_RATE,
|
||||||
channels: 1,
|
channels: 1,
|
||||||
supports_initial_prompt: false,
|
supports_initial_prompt: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use std::collections::HashSet;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::{LazyLock, Mutex};
|
use std::sync::{LazyLock, Mutex};
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::model_registry::{find_model, ModelFile};
|
use lumotia_core::model_registry::{find_model, ModelFile};
|
||||||
use magnotia_core::types::{DownloadProgress, ModelId};
|
use lumotia_core::types::{DownloadProgress, ModelId};
|
||||||
|
|
||||||
static ACTIVE_DOWNLOADS: LazyLock<Mutex<HashSet<String>>> =
|
static ACTIVE_DOWNLOADS: LazyLock<Mutex<HashSet<String>>> =
|
||||||
LazyLock::new(|| Mutex::new(HashSet::new()));
|
LazyLock::new(|| Mutex::new(HashSet::new()));
|
||||||
@@ -40,12 +40,12 @@ impl Drop for DownloadReservation {
|
|||||||
/// Windows: %LOCALAPPDATA%/magnotia/models
|
/// Windows: %LOCALAPPDATA%/magnotia/models
|
||||||
/// Unix: ~/.magnotia/models
|
/// Unix: ~/.magnotia/models
|
||||||
pub fn models_dir() -> PathBuf {
|
pub fn models_dir() -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().models_dir()
|
lumotia_core::paths::app_paths().models_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the directory path where a specific model's files are stored.
|
/// Get the directory path where a specific model's files are stored.
|
||||||
pub fn model_dir(id: &ModelId) -> PathBuf {
|
pub fn model_dir(id: &ModelId) -> PathBuf {
|
||||||
magnotia_core::paths::app_paths().speech_model_dir(id)
|
lumotia_core::paths::app_paths().speech_model_dir(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether all files for a model have been downloaded.
|
/// Check whether all files for a model have been downloaded.
|
||||||
@@ -61,7 +61,7 @@ pub fn is_downloaded(id: &ModelId) -> bool {
|
|||||||
|
|
||||||
/// List all downloaded model IDs.
|
/// List all downloaded model IDs.
|
||||||
pub fn list_downloaded() -> Vec<ModelId> {
|
pub fn list_downloaded() -> Vec<ModelId> {
|
||||||
magnotia_core::model_registry::all_models()
|
lumotia_core::model_registry::all_models()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| is_downloaded(&m.id))
|
.filter(|m| is_downloaded(&m.id))
|
||||||
.map(|m| m.id.clone())
|
.map(|m| m.id.clone())
|
||||||
@@ -117,7 +117,7 @@ fn verified_manifest_path(dir: &Path) -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn verified_manifest_matches(
|
fn verified_manifest_matches(
|
||||||
entry: &magnotia_core::model_registry::ModelEntry,
|
entry: &lumotia_core::model_registry::ModelEntry,
|
||||||
dir: &Path,
|
dir: &Path,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let manifest = match std::fs::read_to_string(verified_manifest_path(dir)) {
|
let manifest = match std::fs::read_to_string(verified_manifest_path(dir)) {
|
||||||
@@ -140,7 +140,7 @@ fn verified_manifest_matches(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_verified_manifest(
|
fn write_verified_manifest(
|
||||||
entry: &magnotia_core::model_registry::ModelEntry,
|
entry: &lumotia_core::model_registry::ModelEntry,
|
||||||
dir: &Path,
|
dir: &Path,
|
||||||
) -> std::io::Result<()> {
|
) -> std::io::Result<()> {
|
||||||
let mut lines = Vec::with_capacity(entry.files.len() + 1);
|
let mut lines = Vec::with_capacity(entry.files.len() + 1);
|
||||||
@@ -357,7 +357,7 @@ mod tests {
|
|||||||
let list = list_downloaded();
|
let list = list_downloaded();
|
||||||
// In test environment, no models are downloaded
|
// In test environment, no models are downloaded
|
||||||
// This just verifies the function doesn't panic
|
// This just verifies the function doesn't panic
|
||||||
assert!(list.len() <= magnotia_core::model_registry::all_models().len());
|
assert!(list.len() <= lumotia_core::model_registry::all_models().len());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -481,7 +481,7 @@ mod tests {
|
|||||||
let file = ModelFile {
|
let file = ModelFile {
|
||||||
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
||||||
url: leak(format!("http://{addr}/fixture.bin")),
|
url: leak(format!("http://{addr}/fixture.bin")),
|
||||||
size: magnotia_core::types::Megabytes(0),
|
size: lumotia_core::types::Megabytes(0),
|
||||||
sha256: leak(expected_sha.clone()),
|
sha256: leak(expected_sha.clone()),
|
||||||
};
|
};
|
||||||
let id = ModelId::new("test-fixture");
|
let id = ModelId::new("test-fixture");
|
||||||
@@ -516,7 +516,7 @@ mod tests {
|
|||||||
let file = ModelFile {
|
let file = ModelFile {
|
||||||
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
||||||
url: leak(format!("http://{addr}/fixture.bin")),
|
url: leak(format!("http://{addr}/fixture.bin")),
|
||||||
size: magnotia_core::types::Megabytes(0),
|
size: lumotia_core::types::Megabytes(0),
|
||||||
sha256: leak(expected_sha),
|
sha256: leak(expected_sha),
|
||||||
};
|
};
|
||||||
let id = ModelId::new("test-fixture");
|
let id = ModelId::new("test-fixture");
|
||||||
@@ -571,7 +571,7 @@ mod tests {
|
|||||||
let file = ModelFile {
|
let file = ModelFile {
|
||||||
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
||||||
url: leak(format!("http://{addr}/fixture.bin")),
|
url: leak(format!("http://{addr}/fixture.bin")),
|
||||||
size: magnotia_core::types::Megabytes(0),
|
size: lumotia_core::types::Megabytes(0),
|
||||||
sha256: leak("0".repeat(64)),
|
sha256: leak("0".repeat(64)),
|
||||||
};
|
};
|
||||||
let id = ModelId::new("test-fixture");
|
let id = ModelId::new("test-fixture");
|
||||||
@@ -599,7 +599,7 @@ mod tests {
|
|||||||
let file = ModelFile {
|
let file = ModelFile {
|
||||||
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
filename: leak(dest.file_name().unwrap().to_string_lossy().into_owned()),
|
||||||
url: leak(format!("http://{addr}/fixture.bin")),
|
url: leak(format!("http://{addr}/fixture.bin")),
|
||||||
size: magnotia_core::types::Megabytes(0),
|
size: lumotia_core::types::Megabytes(0),
|
||||||
sha256: leak("deadbeef".repeat(8)),
|
sha256: leak("deadbeef".repeat(8)),
|
||||||
};
|
};
|
||||||
let id = ModelId::new("test-fixture");
|
let id = ModelId::new("test-fixture");
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use magnotia_cloud_providers::{
|
use lumotia_cloud_providers::{
|
||||||
CostClass, EngineProfile, ProviderCapabilities, ProviderId, ProviderKind, ProviderTranscript,
|
CostClass, EngineProfile, ProviderCapabilities, ProviderId, ProviderKind, ProviderTranscript,
|
||||||
TranscriptionProvider,
|
TranscriptionProvider,
|
||||||
};
|
};
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::types::{AudioSamples, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, TranscriptionOptions};
|
||||||
|
|
||||||
use crate::local_engine::LocalEngine;
|
use crate::local_engine::LocalEngine;
|
||||||
use crate::registry::EngineRegistry;
|
use crate::registry::EngineRegistry;
|
||||||
@@ -68,7 +68,7 @@ impl TranscriptionProvider for LocalProviderAdapter {
|
|||||||
ProviderCapabilities {
|
ProviderCapabilities {
|
||||||
sample_rate: local
|
sample_rate: local
|
||||||
.map(|c| c.sample_rate)
|
.map(|c| c.sample_rate)
|
||||||
.unwrap_or(magnotia_core::constants::WHISPER_SAMPLE_RATE),
|
.unwrap_or(lumotia_core::constants::WHISPER_SAMPLE_RATE),
|
||||||
channels: local.map(|c| c.channels).unwrap_or(1),
|
channels: local.map(|c| c.channels).unwrap_or(1),
|
||||||
initial_prompt_supported: local.map(|c| c.supports_initial_prompt).unwrap_or(false),
|
initial_prompt_supported: local.map(|c| c.supports_initial_prompt).unwrap_or(false),
|
||||||
language_hint_supported: true,
|
language_hint_supported: true,
|
||||||
@@ -153,7 +153,7 @@ mod tests {
|
|||||||
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use magnotia_core::types::{Segment, Transcript};
|
use lumotia_core::types::{Segment, Transcript};
|
||||||
|
|
||||||
/// Mock provider that returns a canned transcript and counts
|
/// Mock provider that returns a canned transcript and counts
|
||||||
/// invocations. Used to validate the orchestrator's dispatch logic
|
/// invocations. Used to validate the orchestrator's dispatch logic
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use magnotia_cloud_providers::{ProviderId, TranscriptionProvider};
|
use lumotia_cloud_providers::{ProviderId, TranscriptionProvider};
|
||||||
|
|
||||||
/// Catalogue of providers known to the orchestrator.
|
/// Catalogue of providers known to the orchestrator.
|
||||||
pub struct EngineRegistry {
|
pub struct EngineRegistry {
|
||||||
@@ -82,11 +82,11 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use magnotia_cloud_providers::{
|
use lumotia_cloud_providers::{
|
||||||
CostClass, EngineProfile, ProviderCapabilities, ProviderKind, ProviderTranscript,
|
CostClass, EngineProfile, ProviderCapabilities, ProviderKind, ProviderTranscript,
|
||||||
};
|
};
|
||||||
use magnotia_core::error::Result;
|
use lumotia_core::error::Result;
|
||||||
use magnotia_core::types::{AudioSamples, Transcript, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, Transcript, TranscriptionOptions};
|
||||||
|
|
||||||
struct DummyProvider {
|
struct DummyProvider {
|
||||||
id: ProviderId,
|
id: ProviderId,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
//! `whisper` feature — `WhisperRsBackend` (direct whisper-rs, the only
|
//! `whisper` feature — `WhisperRsBackend` (direct whisper-rs, the only
|
||||||
//! path that pipes `initial_prompt`).
|
//! path that pipes `initial_prompt`).
|
||||||
|
|
||||||
use magnotia_core::error::Result;
|
use lumotia_core::error::Result;
|
||||||
use magnotia_core::types::{Segment, TranscriptionOptions};
|
use lumotia_core::types::{Segment, TranscriptionOptions};
|
||||||
|
|
||||||
/// Static capabilities a `Transcriber` advertises to callers.
|
/// Static capabilities a `Transcriber` advertises to callers.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ use std::path::Path;
|
|||||||
|
|
||||||
use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters};
|
use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters};
|
||||||
|
|
||||||
use magnotia_core::error::{MagnotiaError, Result};
|
use lumotia_core::error::{MagnotiaError, Result};
|
||||||
use magnotia_core::hardware::vulkan_loader_available;
|
use lumotia_core::hardware::vulkan_loader_available;
|
||||||
use magnotia_core::tuning::{inference_thread_count, Workload};
|
use lumotia_core::tuning::{inference_thread_count, Workload};
|
||||||
use magnotia_core::types::{Segment, TranscriptionOptions};
|
use lumotia_core::types::{Segment, TranscriptionOptions};
|
||||||
|
|
||||||
use crate::transcriber::{Transcriber, TranscriberCapabilities};
|
use crate::transcriber::{Transcriber, TranscriberCapabilities};
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ impl WhisperRsBackend {
|
|||||||
impl Transcriber for WhisperRsBackend {
|
impl Transcriber for WhisperRsBackend {
|
||||||
fn capabilities(&self) -> TranscriberCapabilities {
|
fn capabilities(&self) -> TranscriberCapabilities {
|
||||||
TranscriberCapabilities {
|
TranscriberCapabilities {
|
||||||
sample_rate: magnotia_core::constants::WHISPER_SAMPLE_RATE,
|
sample_rate: lumotia_core::constants::WHISPER_SAMPLE_RATE,
|
||||||
channels: 1,
|
channels: 1,
|
||||||
supports_initial_prompt: true,
|
supports_initial_prompt: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use magnotia_core::hardware::vulkan_loader_available;
|
use lumotia_core::hardware::vulkan_loader_available;
|
||||||
use magnotia_core::tuning::{inference_thread_count, Workload};
|
use lumotia_core::tuning::{inference_thread_count, Workload};
|
||||||
use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters};
|
use whisper_rs::{FullParams, SamplingStrategy, WhisperContext, WhisperContextParameters};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "magnotia"
|
name = "lumotia"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "Magnotia — Think out loud"
|
description = "Lumotia — Think out loud"
|
||||||
authors = ["CORBEL Ltd"]
|
authors = ["CORBEL Ltd"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "magnotia_lib"
|
name = "lumotia_lib"
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
crate-type = ["staticlib", "cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# Default build includes the Whisper backend. Disabling this feature
|
# Default build includes the Whisper backend. Disabling this feature
|
||||||
# also drops it from magnotia-transcription (see Cargo.toml in that crate)
|
# also drops it from lumotia-transcription (see Cargo.toml in that crate)
|
||||||
# so a --no-default-features workspace build does not pull whisper-rs-sys.
|
# so a --no-default-features workspace build does not pull whisper-rs-sys.
|
||||||
# load_model_from_disk returns a runtime error for Engine::Whisper when
|
# load_model_from_disk returns a runtime error for Engine::Whisper when
|
||||||
# this feature is off; Parakeet continues to work.
|
# this feature is off; Parakeet continues to work.
|
||||||
default = ["whisper"]
|
default = ["whisper"]
|
||||||
whisper = ["magnotia-transcription/whisper"]
|
whisper = ["lumotia-transcription/whisper"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
@@ -28,14 +28,14 @@ serde_json = "1"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Workspace crates
|
# Workspace crates
|
||||||
magnotia-core = { path = "../crates/core" }
|
lumotia-core = { path = "../crates/core" }
|
||||||
magnotia-audio = { path = "../crates/audio" }
|
lumotia-audio = { path = "../crates/audio" }
|
||||||
magnotia-transcription = { path = "../crates/transcription", default-features = false }
|
lumotia-transcription = { path = "../crates/transcription", default-features = false }
|
||||||
magnotia-ai-formatting = { path = "../crates/ai-formatting" }
|
lumotia-ai-formatting = { path = "../crates/ai-formatting" }
|
||||||
magnotia-storage = { path = "../crates/storage" }
|
lumotia-storage = { path = "../crates/storage" }
|
||||||
magnotia-cloud-providers = { path = "../crates/cloud-providers" }
|
lumotia-cloud-providers = { path = "../crates/cloud-providers" }
|
||||||
magnotia-hotkey = { path = "../crates/hotkey" }
|
lumotia-hotkey = { path = "../crates/hotkey" }
|
||||||
magnotia-llm = { path = "../crates/llm" }
|
lumotia-llm = { path = "../crates/llm" }
|
||||||
|
|
||||||
# Tauri. The `tray-icon` feature, the global-shortcut/window-state/
|
# Tauri. The `tray-icon` feature, the global-shortcut/window-state/
|
||||||
# autostart plugins are desktop-only — gated below under
|
# autostart plugins are desktop-only — gated below under
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
use crate::commands::security::ensure_main_window;
|
use crate::commands::security::ensure_main_window;
|
||||||
use magnotia_audio::{DeviceInfo, MicrophoneCapture};
|
use lumotia_audio::{DeviceInfo, MicrophoneCapture};
|
||||||
|
|
||||||
/// Enumerate every input device available to cpal, with metadata for the
|
/// Enumerate every input device available to cpal, with metadata for the
|
||||||
/// Settings device-picker UI. Includes a flag for likely PulseAudio /
|
/// Settings device-picker UI. Includes a flag for likely PulseAudio /
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
app_data_dir, crashes_dir, list_recent_errors, log_error, logs_dir, ErrorLogRow,
|
app_data_dir, crashes_dir, list_recent_errors, log_error, logs_dir, ErrorLogRow,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -335,7 +335,7 @@ async fn generate_diagnostic_report_inner(
|
|||||||
|
|
||||||
if opts.include_settings {
|
if opts.include_settings {
|
||||||
out.push_str("## Settings (sanitised)\n\n");
|
out.push_str("## Settings (sanitised)\n\n");
|
||||||
match magnotia_storage::get_setting(&state.db, "magnotia_preferences").await {
|
match lumotia_storage::get_setting(&state.db, "magnotia_preferences").await {
|
||||||
Ok(Some(json)) => {
|
Ok(Some(json)) => {
|
||||||
out.push_str("```json\n");
|
out.push_str("```json\n");
|
||||||
out.push_str(&sanitise_preferences_json(&json));
|
out.push_str(&sanitise_preferences_json(&json));
|
||||||
@@ -520,7 +520,7 @@ pub async fn save_diagnostic_report(
|
|||||||
let _ = app; // reserved for future dialog integration
|
let _ = app; // reserved for future dialog integration
|
||||||
let report = generate_diagnostic_report_inner(&state, options).await?;
|
let report = generate_diagnostic_report_inner(&state, options).await?;
|
||||||
|
|
||||||
let dir = magnotia_storage::app_data_dir().join("diagnostic-reports");
|
let dir = lumotia_storage::app_data_dir().join("diagnostic-reports");
|
||||||
fs::create_dir_all(&dir).map_err(|e| format!("create dir: {e}"))?;
|
fs::create_dir_all(&dir).map_err(|e| format!("create dir: {e}"))?;
|
||||||
|
|
||||||
let ts = SystemTime::now()
|
let ts = SystemTime::now()
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
record_feedback as db_record_feedback, FeedbackTargetType, RecordFeedbackParams,
|
record_feedback as db_record_feedback, FeedbackTargetType, RecordFeedbackParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use magnotia_core::hardware::{self, Os};
|
use lumotia_core::hardware::{self, Os};
|
||||||
use magnotia_core::recommendation;
|
use lumotia_core::recommendation;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
pub struct SystemInfo {
|
pub struct SystemInfo {
|
||||||
@@ -53,7 +53,7 @@ pub fn rank_models() -> Result<Vec<ModelRecommendation>, String> {
|
|||||||
Ok(ranked
|
Ok(ranked
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|scored| {
|
.map(|scored| {
|
||||||
let downloaded = magnotia_transcription::is_downloaded(&scored.entry.id);
|
let downloaded = lumotia_transcription::is_downloaded(&scored.entry.id);
|
||||||
ModelRecommendation {
|
ModelRecommendation {
|
||||||
id: scored.entry.id.as_str().to_string(),
|
id: scored.entry.id.as_str().to_string(),
|
||||||
display_name: scored.entry.display_name,
|
display_name: scored.entry.display_name,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
|
||||||
use magnotia_hotkey::{EvdevHotkeyListener, HotkeyCombo, HotkeyEvent};
|
use lumotia_hotkey::{EvdevHotkeyListener, HotkeyCombo, HotkeyEvent};
|
||||||
|
|
||||||
/// Managed state for the evdev hotkey listener.
|
/// Managed state for the evdev hotkey listener.
|
||||||
pub struct HotkeyState {
|
pub struct HotkeyState {
|
||||||
@@ -30,7 +30,7 @@ pub fn is_wayland_session() -> bool {
|
|||||||
/// Check whether evdev hotkey capture is available (user in `input` group, etc.).
|
/// Check whether evdev hotkey capture is available (user in `input` group, etc.).
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn check_hotkey_access() -> Result<(), String> {
|
pub fn check_hotkey_access() -> Result<(), String> {
|
||||||
magnotia_hotkey::check_evdev_access()
|
lumotia_hotkey::check_evdev_access()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the evdev global hotkey listener. Emits "magnotia:hotkey-pressed" and
|
/// Start the evdev global hotkey listener. Emits "magnotia:hotkey-pressed" and
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
delete_implementation_rule as db_delete_rule, get_task_by_id,
|
delete_implementation_rule as db_delete_rule, get_task_by_id,
|
||||||
insert_implementation_rule as db_insert_rule, list_implementation_rules as db_list_rules,
|
insert_implementation_rule as db_insert_rule, list_implementation_rules as db_list_rules,
|
||||||
mark_implementation_rule_fired as db_mark_rule_fired,
|
mark_implementation_rule_fired as db_mark_rule_fired,
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ use crate::commands::models::{default_model_id_for_engine, ensure_model_loaded};
|
|||||||
use crate::commands::power::PowerAssertion;
|
use crate::commands::power::PowerAssertion;
|
||||||
use crate::commands::security::ensure_main_window;
|
use crate::commands::security::ensure_main_window;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use magnotia_ai_formatting::{post_process_segments, FormatMode, PostProcessOptions};
|
use lumotia_ai_formatting::{post_process_segments, FormatMode, PostProcessOptions};
|
||||||
use magnotia_audio::{
|
use lumotia_audio::{
|
||||||
AudioChunk, CaptureRuntimeError, MicrophoneCapture, StreamingResampler, WavWriter,
|
AudioChunk, CaptureRuntimeError, MicrophoneCapture, StreamingResampler, WavWriter,
|
||||||
};
|
};
|
||||||
use magnotia_core::constants::WHISPER_SAMPLE_RATE;
|
use lumotia_core::constants::WHISPER_SAMPLE_RATE;
|
||||||
use magnotia_core::types::{AudioSamples, Segment, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, Segment, TranscriptionOptions};
|
||||||
use magnotia_transcription::LocalEngine;
|
use lumotia_transcription::LocalEngine;
|
||||||
|
|
||||||
const CHUNK_SAMPLES: usize = 32_000; // 2s at 16kHz
|
const CHUNK_SAMPLES: usize = 32_000; // 2s at 16kHz
|
||||||
const OVERLAP_SAMPLES: usize = 4_000; // 0.25s at 16kHz
|
const OVERLAP_SAMPLES: usize = 4_000; // 0.25s at 16kHz
|
||||||
@@ -450,7 +450,7 @@ struct InferenceTask {
|
|||||||
chunk_start_sample: u64,
|
chunk_start_sample: u64,
|
||||||
trim_before_secs: f64,
|
trim_before_secs: f64,
|
||||||
duration_secs: f64,
|
duration_secs: f64,
|
||||||
rx: std::sync::mpsc::Receiver<Result<magnotia_transcription::TimedTranscript, String>>,
|
rx: std::sync::mpsc::Receiver<Result<lumotia_transcription::TimedTranscript, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -503,15 +503,15 @@ pub async fn start_live_transcription_session(
|
|||||||
let resolved_profile_id = config
|
let resolved_profile_id = config
|
||||||
.profile_id
|
.profile_id
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or_else(|| magnotia_storage::DEFAULT_PROFILE_ID.to_string());
|
.unwrap_or_else(|| lumotia_storage::DEFAULT_PROFILE_ID.to_string());
|
||||||
|
|
||||||
let profile = magnotia_storage::database::get_profile(&state.db, &resolved_profile_id)
|
let profile = lumotia_storage::database::get_profile(&state.db, &resolved_profile_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.ok_or_else(|| format!("Profile {resolved_profile_id} not found"))?;
|
.ok_or_else(|| format!("Profile {resolved_profile_id} not found"))?;
|
||||||
|
|
||||||
let profile_terms: Vec<String> =
|
let profile_terms: Vec<String> =
|
||||||
magnotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
lumotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -1555,8 +1555,8 @@ mod tests {
|
|||||||
let stop_flag = Arc::new(AtomicBool::new(false));
|
let stop_flag = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let (tx1, rx1) = std::sync::mpsc::channel();
|
let (tx1, rx1) = std::sync::mpsc::channel();
|
||||||
tx1.send(Ok(magnotia_transcription::TimedTranscript {
|
tx1.send(Ok(lumotia_transcription::TimedTranscript {
|
||||||
transcript: magnotia_core::types::Transcript::new(
|
transcript: lumotia_core::types::Transcript::new(
|
||||||
vec![segment(0.0, 0.8, "first chunk")],
|
vec![segment(0.0, 0.8, "first chunk")],
|
||||||
"en".into(),
|
"en".into(),
|
||||||
0.8,
|
0.8,
|
||||||
@@ -1597,8 +1597,8 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let (tx2, rx2) = std::sync::mpsc::channel();
|
let (tx2, rx2) = std::sync::mpsc::channel();
|
||||||
tx2.send(Ok(magnotia_transcription::TimedTranscript {
|
tx2.send(Ok(lumotia_transcription::TimedTranscript {
|
||||||
transcript: magnotia_core::types::Transcript::new(
|
transcript: lumotia_core::types::Transcript::new(
|
||||||
vec![segment(0.0, 0.9, "second chunk")],
|
vec![segment(0.0, 0.9, "second chunk")],
|
||||||
"en".into(),
|
"en".into(),
|
||||||
0.9,
|
0.9,
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ use tauri::{Emitter, State};
|
|||||||
use crate::commands::power::PowerAssertion;
|
use crate::commands::power::PowerAssertion;
|
||||||
use crate::commands::security::ensure_main_window;
|
use crate::commands::security::ensure_main_window;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use magnotia_ai_formatting::{llm_cleanup_text, LlmPromptPreset};
|
use lumotia_ai_formatting::{llm_cleanup_text, LlmPromptPreset};
|
||||||
use magnotia_core::hardware;
|
use lumotia_core::hardware;
|
||||||
use magnotia_llm::model_manager::{self, model_info};
|
use lumotia_llm::model_manager::{self, model_info};
|
||||||
use magnotia_llm::{ContentTags, LlmModelId};
|
use lumotia_llm::{ContentTags, LlmModelId};
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -369,9 +369,9 @@ pub async fn cleanup_transcript_text_cmd(
|
|||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
ensure_main_window(&window)?;
|
ensure_main_window(&window)?;
|
||||||
let resolved_profile_id =
|
let resolved_profile_id =
|
||||||
profile_id.unwrap_or_else(|| magnotia_storage::DEFAULT_PROFILE_ID.to_string());
|
profile_id.unwrap_or_else(|| lumotia_storage::DEFAULT_PROFILE_ID.to_string());
|
||||||
let profile_terms: Vec<String> =
|
let profile_terms: Vec<String> =
|
||||||
magnotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
lumotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use magnotia_core::process_watch::{self, ProcessLister};
|
use lumotia_core::process_watch::{self, ProcessLister};
|
||||||
|
|
||||||
/// Tauri-managed state for the meeting poller. Holds a long-lived
|
/// Tauri-managed state for the meeting poller. Holds a long-lived
|
||||||
/// `ProcessLister` so each poll refreshes the existing `sysinfo::System`
|
/// `ProcessLister` so each poll refreshes the existing `sysinfo::System`
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ use tauri::Emitter;
|
|||||||
|
|
||||||
use crate::commands::security::ensure_main_window;
|
use crate::commands::security::ensure_main_window;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use magnotia_core::constants::WHISPER_SAMPLE_RATE;
|
use lumotia_core::constants::WHISPER_SAMPLE_RATE;
|
||||||
use magnotia_core::hardware::{self, vulkan_loader_available, CpuFeatures};
|
use lumotia_core::hardware::{self, vulkan_loader_available, CpuFeatures};
|
||||||
use magnotia_core::model_registry::{self, Engine, LanguageSupport, ModelEntry};
|
use lumotia_core::model_registry::{self, Engine, LanguageSupport, ModelEntry};
|
||||||
use magnotia_core::types::{AudioSamples, ModelId, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, ModelId, TranscriptionOptions};
|
||||||
#[cfg(feature = "whisper")]
|
#[cfg(feature = "whisper")]
|
||||||
use magnotia_transcription::load_whisper;
|
use lumotia_transcription::load_whisper;
|
||||||
use magnotia_transcription::model_manager;
|
use lumotia_transcription::model_manager;
|
||||||
use magnotia_transcription::{load_parakeet, LocalEngine, Transcriber};
|
use lumotia_transcription::{load_parakeet, LocalEngine, Transcriber};
|
||||||
|
|
||||||
/// Map legacy size strings to ModelId.
|
/// Map legacy size strings to ModelId.
|
||||||
fn whisper_model_id(size: &str) -> ModelId {
|
fn whisper_model_id(size: &str) -> ModelId {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Tauri commands wrapping magnotia_storage profile + profile_term CRUD.
|
// Tauri commands wrapping lumotia_storage profile + profile_term CRUD.
|
||||||
// Pattern mirrors tasks.rs — flat imports from `magnotia_storage` with a `db_`
|
// Pattern mirrors tasks.rs — flat imports from `lumotia_storage` with a `db_`
|
||||||
// alias prefix to avoid name collisions with the command functions, plain
|
// alias prefix to avoid name collisions with the command functions, plain
|
||||||
// snake_case parameters (Tauri 2.x auto-converts camelCase JS keys),
|
// snake_case parameters (Tauri 2.x auto-converts camelCase JS keys),
|
||||||
// `.map_err(|e| e.to_string())` for error conversion, and camelCase DTOs
|
// `.map_err(|e| e.to_string())` for error conversion, and camelCase DTOs
|
||||||
@@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use magnotia_ai_formatting::extract_corrections;
|
use lumotia_ai_formatting::extract_corrections;
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
add_profile_term as db_add_profile_term, create_profile as db_create_profile,
|
add_profile_term as db_add_profile_term, create_profile as db_create_profile,
|
||||||
delete_profile as db_delete_profile, delete_profile_term as db_delete_profile_term,
|
delete_profile as db_delete_profile, delete_profile_term as db_delete_profile_term,
|
||||||
list_profile_terms as db_list_profile_terms, list_profiles as db_list_profiles,
|
list_profile_terms as db_list_profile_terms, list_profiles as db_list_profiles,
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
//! filtering rather than adding a second query path.
|
//! filtering rather than adding a second query path.
|
||||||
//!
|
//!
|
||||||
//! Stored under the existing SQLite settings table via
|
//! Stored under the existing SQLite settings table via
|
||||||
//! `magnotia_storage::{get_setting, set_setting}` — same bag as
|
//! `lumotia_storage::{get_setting, set_setting}` — same bag as
|
||||||
//! `magnotia_preferences`.
|
//! `magnotia_preferences`.
|
||||||
|
|
||||||
use magnotia_storage::{get_setting, set_setting};
|
use lumotia_storage::{get_setting, set_setting};
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Tauri commands wrapping magnotia_storage task CRUD.
|
// Tauri commands wrapping lumotia_storage task CRUD.
|
||||||
// Pattern mirrors transcripts.rs — TaskDto is the camelCase frontend shape,
|
// Pattern mirrors transcripts.rs — TaskDto is the camelCase frontend shape,
|
||||||
// storage functions are aliased with db_ prefix to avoid name collisions.
|
// storage functions are aliased with db_ prefix to avoid name collisions.
|
||||||
|
|
||||||
@@ -6,8 +6,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use magnotia_llm::prompts::FeedbackExample as LlmFeedbackExample;
|
use lumotia_llm::prompts::FeedbackExample as LlmFeedbackExample;
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
complete_subtask_and_check_parent as db_complete_subtask, complete_task as db_complete_task,
|
complete_subtask_and_check_parent as db_complete_subtask, complete_task as db_complete_task,
|
||||||
delete_task as db_delete_task, get_task_by_id as db_get_task,
|
delete_task as db_delete_task, get_task_by_id as db_get_task,
|
||||||
insert_subtask as db_insert_subtask, insert_task as db_insert_task,
|
insert_subtask as db_insert_subtask, insert_task as db_insert_task,
|
||||||
@@ -228,7 +228,7 @@ fn to_llm_examples(rows: Vec<FeedbackRow>) -> Vec<LlmFeedbackExample> {
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
target: "magnotia_lib::feedback",
|
target: "lumotia_lib::feedback",
|
||||||
row_id = r.id,
|
row_id = r.id,
|
||||||
error = %e,
|
error = %e,
|
||||||
"skipping feedback row with malformed context_json"
|
"skipping feedback row with malformed context_json"
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ use crate::commands::build_initial_prompt;
|
|||||||
use crate::commands::models::{default_model_id_for_engine, ensure_model_loaded};
|
use crate::commands::models::{default_model_id_for_engine, ensure_model_loaded};
|
||||||
use crate::commands::security::ensure_main_window;
|
use crate::commands::security::ensure_main_window;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use magnotia_ai_formatting::{post_process_segments, FormatMode, PostProcessOptions};
|
use lumotia_ai_formatting::{post_process_segments, FormatMode, PostProcessOptions};
|
||||||
use magnotia_core::constants::WHISPER_SAMPLE_RATE;
|
use lumotia_core::constants::WHISPER_SAMPLE_RATE;
|
||||||
use magnotia_core::types::{AudioSamples, Segment, Transcript, TranscriptionOptions};
|
use lumotia_core::types::{AudioSamples, Segment, Transcript, TranscriptionOptions};
|
||||||
|
|
||||||
const PARAKEET_CHUNK_THRESHOLD_SECS: usize = 18;
|
const PARAKEET_CHUNK_THRESHOLD_SECS: usize = 18;
|
||||||
const PARAKEET_CHUNK_SECS: usize = 15;
|
const PARAKEET_CHUNK_SECS: usize = 15;
|
||||||
@@ -29,7 +29,7 @@ struct ChunkingStrategy {
|
|||||||
fn pick_engine(
|
fn pick_engine(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
engine: &str,
|
engine: &str,
|
||||||
) -> Result<Arc<magnotia_transcription::LocalEngine>, String> {
|
) -> Result<Arc<lumotia_transcription::LocalEngine>, String> {
|
||||||
match engine {
|
match engine {
|
||||||
"whisper" => Ok(state.whisper_engine.clone()),
|
"whisper" => Ok(state.whisper_engine.clone()),
|
||||||
"parakeet" => Ok(state.parakeet_engine.clone()),
|
"parakeet" => Ok(state.parakeet_engine.clone()),
|
||||||
@@ -70,11 +70,11 @@ fn trim_overlap_segments(segments: &mut Vec<Segment>, trim_before_secs: f64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transcribe_samples_sync(
|
fn transcribe_samples_sync(
|
||||||
engine: Arc<magnotia_transcription::LocalEngine>,
|
engine: Arc<lumotia_transcription::LocalEngine>,
|
||||||
engine_name: &str,
|
engine_name: &str,
|
||||||
samples: Vec<f32>,
|
samples: Vec<f32>,
|
||||||
options: TranscriptionOptions,
|
options: TranscriptionOptions,
|
||||||
) -> Result<magnotia_transcription::TimedTranscript, String> {
|
) -> Result<lumotia_transcription::TimedTranscript, String> {
|
||||||
let Some(strategy) = pick_chunking_strategy(engine_name, samples.len()) else {
|
let Some(strategy) = pick_chunking_strategy(engine_name, samples.len()) else {
|
||||||
let audio = AudioSamples::mono_16khz(samples);
|
let audio = AudioSamples::mono_16khz(samples);
|
||||||
return engine
|
return engine
|
||||||
@@ -128,7 +128,7 @@ fn transcribe_samples_sync(
|
|||||||
chunk_start = chunk_end.saturating_sub(strategy.overlap_samples);
|
chunk_start = chunk_end.saturating_sub(strategy.overlap_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(magnotia_transcription::TimedTranscript {
|
Ok(lumotia_transcription::TimedTranscript {
|
||||||
transcript: Transcript::new(
|
transcript: Transcript::new(
|
||||||
all_segments,
|
all_segments,
|
||||||
options.language.clone().unwrap_or_else(|| "en".to_string()),
|
options.language.clone().unwrap_or_else(|| "en".to_string()),
|
||||||
@@ -165,15 +165,15 @@ pub async fn transcribe_file(
|
|||||||
) -> Result<serde_json::Value, String> {
|
) -> Result<serde_json::Value, String> {
|
||||||
ensure_main_window(&window)?;
|
ensure_main_window(&window)?;
|
||||||
let resolved_profile_id =
|
let resolved_profile_id =
|
||||||
profile_id.unwrap_or_else(|| magnotia_storage::DEFAULT_PROFILE_ID.to_string());
|
profile_id.unwrap_or_else(|| lumotia_storage::DEFAULT_PROFILE_ID.to_string());
|
||||||
|
|
||||||
let profile = magnotia_storage::database::get_profile(&state.db, &resolved_profile_id)
|
let profile = lumotia_storage::database::get_profile(&state.db, &resolved_profile_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.ok_or_else(|| format!("Profile {resolved_profile_id} not found"))?;
|
.ok_or_else(|| format!("Profile {resolved_profile_id} not found"))?;
|
||||||
|
|
||||||
let profile_terms: Vec<String> =
|
let profile_terms: Vec<String> =
|
||||||
magnotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
lumotia_storage::database::list_profile_terms(&state.db, &resolved_profile_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -199,7 +199,7 @@ pub async fn transcribe_file(
|
|||||||
let engine_name_for_worker = engine_name.clone();
|
let engine_name_for_worker = engine_name.clone();
|
||||||
let path_for_probe = Path::new(&path);
|
let path_for_probe = Path::new(&path);
|
||||||
if let Some(duration_secs) =
|
if let Some(duration_secs) =
|
||||||
magnotia_audio::probe_audio_duration_secs(path_for_probe).map_err(|e| e.to_string())?
|
lumotia_audio::probe_audio_duration_secs(path_for_probe).map_err(|e| e.to_string())?
|
||||||
{
|
{
|
||||||
if duration_secs > MAX_FILE_TRANSCRIPTION_SECS {
|
if duration_secs > MAX_FILE_TRANSCRIPTION_SECS {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@@ -210,12 +210,12 @@ pub async fn transcribe_file(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let timed = tokio::task::spawn_blocking(move || {
|
let timed = tokio::task::spawn_blocking(move || {
|
||||||
let audio = magnotia_audio::decode_audio_file_limited(
|
let audio = lumotia_audio::decode_audio_file_limited(
|
||||||
Path::new(&path),
|
Path::new(&path),
|
||||||
Some(MAX_FILE_TRANSCRIPTION_SECS),
|
Some(MAX_FILE_TRANSCRIPTION_SECS),
|
||||||
)
|
)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let resampled = magnotia_audio::resample_to_16khz(&audio).map_err(|e| e.to_string())?;
|
let resampled = lumotia_audio::resample_to_16khz(&audio).map_err(|e| e.to_string())?;
|
||||||
transcribe_samples_sync(
|
transcribe_samples_sync(
|
||||||
engine,
|
engine,
|
||||||
&engine_name_for_worker,
|
&engine_name_for_worker,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Tauri commands wrapping the magnotia_storage transcript CRUD.
|
// Tauri commands wrapping the lumotia_storage transcript CRUD.
|
||||||
// These are the bridge that lets the Svelte frontend treat SQLite as the
|
// These are the bridge that lets the Svelte frontend treat SQLite as the
|
||||||
// canonical store rather than localStorage.
|
// canonical store rather than localStorage.
|
||||||
//
|
//
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use magnotia_storage::{
|
use lumotia_storage::{
|
||||||
delete_transcript as db_delete_transcript, get_transcript as db_get_transcript,
|
delete_transcript as db_delete_transcript, get_transcript as db_get_transcript,
|
||||||
insert_transcript as db_insert_transcript, list_transcripts_paged,
|
insert_transcript as db_insert_transcript, list_transcripts_paged,
|
||||||
search_transcripts as db_search_transcripts, update_transcript as db_update_transcript,
|
search_transcripts as db_search_transcripts, update_transcript as db_update_transcript,
|
||||||
@@ -110,7 +110,7 @@ pub async fn add_transcript(
|
|||||||
profile_id: transcript
|
profile_id: transcript
|
||||||
.profile_id
|
.profile_id
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.unwrap_or(magnotia_storage::DEFAULT_PROFILE_ID),
|
.unwrap_or(lumotia_storage::DEFAULT_PROFILE_ID),
|
||||||
title: transcript.title.as_deref(),
|
title: transcript.title.as_deref(),
|
||||||
audio_path: transcript.audio_path.as_deref(),
|
audio_path: transcript.audio_path.as_deref(),
|
||||||
duration: transcript.duration,
|
duration: transcript.duration,
|
||||||
|
|||||||
@@ -12,15 +12,15 @@ use sqlx::SqlitePool;
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use magnotia_core::types::EngineName;
|
use lumotia_core::types::EngineName;
|
||||||
use magnotia_llm::LlmEngine;
|
use lumotia_llm::LlmEngine;
|
||||||
use magnotia_storage::{database_path, get_setting, init as init_db, prune_error_log, set_setting};
|
use lumotia_storage::{database_path, get_setting, init as init_db, prune_error_log, set_setting};
|
||||||
|
|
||||||
/// How long to retain `error_log` rows. Pruned once on startup.
|
/// How long to retain `error_log` rows. Pruned once on startup.
|
||||||
/// 90 days is long enough to triage "this happened a few weeks ago"
|
/// 90 days is long enough to triage "this happened a few weeks ago"
|
||||||
/// reports and short enough that the table stays kilobyte-scale.
|
/// reports and short enough that the table stays kilobyte-scale.
|
||||||
const ERROR_LOG_RETENTION_DAYS: i64 = 90;
|
const ERROR_LOG_RETENTION_DAYS: i64 = 90;
|
||||||
use magnotia_transcription::LocalEngine;
|
use lumotia_transcription::LocalEngine;
|
||||||
|
|
||||||
static TRACING_INIT: Once = Once::new();
|
static TRACING_INIT: Once = Once::new();
|
||||||
|
|
||||||
@@ -162,7 +162,7 @@ fn init_tracing() {
|
|||||||
TRACING_INIT.call_once(|| {
|
TRACING_INIT.call_once(|| {
|
||||||
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| {
|
||||||
EnvFilter::new(
|
EnvFilter::new(
|
||||||
"warn,magnotia=info,magnotia_lib=info,magnotia_audio=info,magnotia_startup=info,magnotia_hotkey=info,magnotia_ai_formatting=info",
|
"warn,magnotia=info,lumotia_lib=info,lumotia_audio=info,magnotia_startup=info,lumotia_hotkey=info,lumotia_ai_formatting=info",
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
magnotia_lib::run()
|
lumotia_lib::run()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user