Commit Graph

14 Commits

Author SHA1 Message Date
089349d966 agent: lumotia-rebrand — rust workspace crates magnotia-* -> lumotia-*
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
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>
2026-05-13 08:48:09 +01:00
6bc8acccce feat(transcription): Phase A — engine protocol + registry + orchestrator (clean-room)
Introduces the architectural spine for the Wyrdnote engine layer per
the spec at outputs/wyrdnote/2026-05-10-engine-architecture-spec.md
in the CORBEL-Main workspace, Codex-reviewed 2026/05/10.

Clean-room derivative of VoiceInk's TranscriptionService /
TranscriptionServiceRegistry / TranscriptionPipeline shape. Built from
the architectural pattern only; no GPL-3.0 code, comments, or
identifiers lifted. Wyrdnote names, control flow, and trait signatures
are original.

What this lands:

crates/cloud-providers/src/provider.rs (new, 184 lines)
  TranscriptionProvider async trait. Object-safe via async_trait so
  Arc<dyn TranscriptionProvider> is the canonical shape. Lives in
  cloud-providers (not transcription) per D7 so an OEM licensee
  implementing the trait depends only on this crate plus magnotia-core
  (no transcription internals leaked through the trait surface).
  Surrounding types: ProviderId (lower-kebab-case), ProviderKind,
  NetworkRequirement, CostClass, ProviderCapabilities, EngineProfile,
  ProviderTranscript. Compile-time object-safety witness in tests.

crates/cloud-providers/Cargo.toml (modified)
  Adds async-trait + serde dependencies.

crates/cloud-providers/src/lib.rs (modified)
  Re-exports the provider surface.

crates/transcription/src/registry.rs (new, 183 lines)
  EngineRegistry: catalogue of providers keyed by ProviderId. Push-style
  registration, default-provider id captured at construction. Read-only
  after boot. Full unit-test coverage: empty default, register/get
  round-trip, default-resolves-after-registration, re-register replaces,
  ids enumeration.

crates/transcription/src/orchestrator.rs (new, 286 lines)
  LocalProviderAdapter wraps Arc<LocalEngine>, presents async
  TranscriptionProvider upward via tokio::task::spawn_blocking. Per D7
  the adapter lives in the orchestrator, NOT as impl TranscriptionProvider
  for LocalEngine — this keeps the dependency edge one-directional and
  avoids leaking async-runtime requirements onto the synchronous
  Transcriber trait.

  Orchestrator: single transcribe() entry point; resolves a provider
  from the registry, derives TranscriptionOptions from the EngineProfile,
  dispatches. Returns a clear error when an unregistered provider is
  named. Unit tests use a mock CannedProvider to validate dispatch,
  error handling, and option routing without booting a real model.

crates/transcription/Cargo.toml (modified)
  Depends on magnotia-cloud-providers + async-trait. Dev-dep tokio
  gains macros + rt-multi-thread features for #[tokio::test].

crates/transcription/src/lib.rs (modified)
  Exports Orchestrator, EngineRegistry, LocalProviderAdapter, and
  re-exports the provider trait surface so downstream crates depend
  only on magnotia-transcription for both local engines and the trait.

KNOWN-ISSUES.md (modified)
  Adds KI-06: existing dictation, live, and meeting commands still call
  LocalEngine::transcribe_sync directly via pick_engine. The orchestrator
  path is dormant until a Phase A.1 follow-up commit migrates the call
  sites. Cloud providers (Phase G) cannot be exercised end-to-end until
  the rewire lands. Phasing rationale: this commit lands the abstractions
  cleanly with full test coverage; the rewire is a separate diff so the
  chunking + post-processing logic in transcribe_file moves without
  inflating this commit beyond review fidelity.

Test results: 9 new tests pass (orchestrator: dispatches, errors-on-
unregistered, routes-initial-prompt, object-safe; registry: empty-default,
round-trip, default-resolves, re-register, ids-list). Pre-existing
59 transcription tests + 5 cloud-providers tests still green.
cargo check --workspace clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 07:49:24 +01:00
9b0067b4c0 Land release blocker fixes and workspace cleanup
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
2026-04-23 00:16:09 +01:00
cea15c12c7 feat(A.3 #25): aggressive buffer trim tied to commit points
New streaming::buffer_trim module with two pure helpers:

- sample_index_for_seconds(end_secs, sample_rate) -> u64: converts
  LocalAgreement::last_committed_end_secs() into an absolute sample
  index. Defensive against negative end_secs (treats as 0) so a future
  clock-skewed timestamp cannot wrap to a huge u64.
- trim_buffer_to_commit_point(buffer, buffer_start_sample,
  commit_sample_index) -> new_buffer_start_sample: drains the prefix
  of the capture buffer that falls below the commit point and returns
  the new absolute-index origin.

Edge cases covered by tests:
- commit before buffer start → no drain
- commit equal to buffer start → no drain
- commit inside buffer → drain prefix, advance origin
- commit at buffer end → drain all, origin moves forward
- commit past buffer end → drain all, origin parks at commit (rare
  edge after a committer reset)
- sample_index_for_seconds rounds nearest, negatives clamp to 0
- integration with LocalAgreement::last_committed_end_secs

trim_bounds_buffer_over_long_session is the acceptance fixture for
the ufal #120/#102 failure mode: 100 cycles of 16_000 captured samples
with a 200-sample tentative tail per cycle, and the buffer stays below
2× the tentative envelope instead of growing to 1.6M samples.

Integration into src-tauri/src/commands/live.rs deferred to the
dogfood session that wires VadChunker and LocalAgreement end-to-end —
the trim is a one-line replacement at the maybe_dispatch_chunk drain
site once the committer is feeding it commit points.
2026-04-22 07:52:48 +01:00
da2340325f feat(A.3 #24): LocalAgreement-n commit policy
New streaming::commit_policy module implementing ufal's
LocalAgreement-n pattern: tokens emitted by the streaming ASR
pipeline stay tentative until N consecutive passes produce the same
prefix, at which point the agreed prefix commits.

Types:
- Token: text + absolute start/end seconds. PartialEq is text-only so
  identical words from overlapping Whisper windows compare equal even
  when their timestamps drift.
- CommitDecision { newly_committed, tentative }: the partition fed
  back to the live-session worker after each pass.
- CommitPolicy enum with LocalAgreement { n }. Default is n=2 (ufal).
- LocalAgreement: stateful committer with push/flush/reset and a
  last_committed_end_secs accessor. Brief item #25 uses the latter
  to compute the sample-index drain target for aggressive buffer trim.

Invariants exercised by tests:
- first pass is all tentative (need 2 passes to commit under n=2)
- two matching passes commit their common prefix
- divergent second pass commits nothing
- extending agreement commits only the newly-agreed tokens
- tentative tail tracks latest pass only (no stale guesses)
- committed prefix never shrinks, even if later passes contradict
- flush emits any tentative-but-not-committed tail at session end
- flush on empty history is a no-op
- reset clears all commit state
- n=3 requires three matching passes before anything commits
- CommitPolicy::default() is LocalAgreement { n: 2 }

Integration into src-tauri/src/commands/live.rs deferred — the
tentative/committed split needs the B-side 'tentative: bool' field on
LiveResultMessage.segments (workstream-B #24 UI contract) and
validation against real streaming captures before going live.
2026-04-22 07:50:18 +01:00
05eea41649 feat(A.3 #21): VadChunker trait + RMS backend (Silero deferred)
New crates/transcription/src/streaming/ module with:

- VadChunker trait: Send-bound, object-safe, push/flush/reset/
  next_sample_index. Same surface a future Silero backend will
  present, so live.rs wiring does not change when Silero drops in.
- VadChunk type: (start_sample: u64, samples: Vec<f32>) for
  commit-policy sample-offset bookkeeping in #24.
- RmsVadChunker: fallback backend the plan permits while the
  ort 2.0.0-rc.10 vs rc.12 ecosystem conflict blocks silero-vad-rust
  / voice_activity_detector. Tuned to match the existing
  evaluate_speech_gate behaviour (enter 0.003, exit 0.0014, 3-frame
  onset, 500 ms silence close, 2 s max chunk).

Key behavioural properties, each backed by a test:
- pure silence emits nothing
- samples between exit and enter thresholds never trigger onset
- a single loud frame does not start a chunk (sustained speech only)
- sustained speech followed by silence emits exactly one chunk
- hysteresis: a dip between enter and exit does not split a chunk
- max_chunk_samples caps continuous speech (Whisper never fed > 2 s)
- flush surfaces in-flight speech
- flush on an idle chunker emits nothing
- reset restores a clean state
- emitted chunk start_sample includes the onset buffer (Whisper sees
  the speech attack, not post-onset audio)

Open items tracked as follow-ups:
1. Silero backend via direct ort rc.12 bridge (Handy-style). Blocked
   on either ecosystem ort alignment or dedicated bridge session.
2. Integration into src-tauri/src/commands/live.rs. Deferred so
   threshold tuning can be validated against real microphone
   captures rather than synthetic constant-signal fixtures.
2026-04-22 07:47:30 +01:00
8b49d0fe9c feat(A.2 #13): replace SpeechBackend enum with Transcriber trait
New crates/transcription/src/transcriber.rs defines a Transcriber
trait (Send supertrait for spawn_blocking travel) with
TranscriberCapabilities (sample_rate, channels, supports_initial_prompt).
TranscriberCapabilities.sample_rate is load-bearing for the upcoming
progressive WAV writer (#19).

Concrete impls:
- SpeechModelAdapter wraps Box<dyn transcribe_rs::SpeechModel + Send>
  for Parakeet (and any future transcribe-rs-backed engine).
- WhisperRsBackend moves its transcribe_sync body into the impl,
  widening the signature from &self to &mut self so per-call
  WhisperState can be created cleanly through the trait object.

LocalEngine now holds Box<dyn Transcriber + Send>; dispatch in
transcribe_sync collapses from a match to a direct call. Adds
LocalEngine::capabilities() for the WAV-writer.

Cargo feature flag "whisper" (default on) makes whisper-rs, num_cpus,
and the whole whisper_rs_backend module optional. cargo check
--no-default-features -p kon-transcription now builds without pulling
whisper-rs-sys — the escape hatch brief item #6 / #13 called for on
Windows / non-AVX2 / cloud-only builds. load_whisper is cfg-gated
behind the same feature.

src-tauri/src/commands/models.rs load_model_from_disk returns
Box<dyn Transcriber + Send> instead of SpeechBackend; caller chain
(ensure_model_loaded, prewarm_default_model) is unchanged.

transcriber_trait_is_object_safe test lands alongside the trait as a
compile-time witness against future Self-returning / generic-method
additions.
2026-04-22 04:33:23 +01:00
34fce3cf9e feat: OpenWhispr-inspired transcription polish pass
Some checks failed
check / cargo check (macos-latest) (push) Has been cancelled
check / cargo check (ubuntu-22.04) (push) Has been cancelled
check / cargo check (windows-latest) (push) Has been cancelled
check / svelte build + lint (push) Has been cancelled
Major quality pass on top of Phase 2. Five substantive changes plus
cross-cutting touches across audio, hotkey, transcription, and Tauri
command layers.

  Transcription quality

  - Long-audio chunking in commands/transcription.rs: Parakeet and large
    file transcription now chunk-and-recompose with overlap trimming, so
    the live-path chunking advantage extends to file-based workflows.
  - Stateful live speech gate in commands/live.rs on top of the earlier
    duplicate-boundary filtering — distinguishes start-of-speech from
    mid-speech and holds state across chunks.

  Auto-learning corrections

  - New crates/ai-formatting/src/correction_learning.rs: extracts user
    text corrections from viewer edits and proposes additions to the
    active profile's vocabulary.
  - src-tauri/src/commands/profiles.rs bridge for frontend-driven
    confirmation of learned terms.
  - src/routes/viewer/+page.svelte hooks the learning path into the
    segment-edit flow so corrections feed profile_terms without a
    separate 'train this profile' UX.

  Transcript profile provenance

  - Migration v8 (crates/storage/src/migrations.rs) adds profile_id to
    transcripts, defaulting to DEFAULT_PROFILE_ID so existing rows stay
    valid.
  - crates/storage/src/database.rs: TranscriptRow + CRUD carry profile_id.
  - src-tauri/src/commands/transcripts.rs: add_transcript accepts and
    persists profile_id.
  - DictationPage.svelte + FilesPage.svelte send activeProfileId on
    capture so learned corrections are attributed to the right profile.

  Cleanup prompt contract

  - crates/ai-formatting/src/llm_client.rs hardened: the CLEANUP_PROMPT
    now specifies concrete do/do-not rules, ready for a real model-backed
    cleanup pass. The llm_client is still a stub — kon-llm remains unwired
    — but the prompt shape is final.

  Cross-cutting polish

  - Minor touches in audio (capture/decode/resample), hotkey (lib/linux/stub),
    core, transcription (concurrency/model_manager/local_engine/whisper_rs),
    and the rest of src-tauri/src/commands/*: error-path tightening, log
    clarity, TS-migration follow-ups (@ts-nocheck additions for incremental
    typing).

Verified locally: npm run check, cargo test -p kon-ai-formatting,
cargo test -p kon-storage, cargo test -p kon --lib commands::live::tests,
cargo check — all green.

Scope boundary: kon-llm crate is still a stub; task extraction remains
rule-based. Bundled local-LLM runtime is the next clean step and is not
in this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 22:39:08 +01:00
4256383a5b refactor(transcription): LocalEngine dispatches SpeechBackend enum — Whisper now on whisper-rs 2026-04-19 20:20:03 +01:00
c426fa7eb2 feat(transcription): add WhisperRsBackend wrapping whisper-rs with initial_prompt support 2026-04-19 20:16:07 +01:00
cca79dec4c fix: SQL trigger-aware migration splitter, SpeechModel re-export, i64 type alignment
crates/storage/src/migrations.rs:
  Replace naive sql.split(';') with split_statements() that tracks
  BEGIN...END depth, so migrations containing trigger definitions
  execute correctly instead of being split mid-block.

crates/transcription/src/lib.rs:
  Re-export transcribe_rs::SpeechModel so callers in src-tauri can
  reference it without adding transcribe-rs as a direct dependency.

src-tauri/src/commands/models.rs:
  Use Box<dyn SpeechModel + Send> as the load_model_from_disk return
  type, matching the trait object that transcription crates produce.

src-tauri/src/commands/transcripts.rs:
  Remove the stale .map(|v| v as i32) casts on sample_rate and
  audio_channels. InsertTranscriptParams now stores these as i64
  (ebf449b), matching the i64 fields on CreateTranscriptRequest;
  casting to i32 first would silently truncate large sample rates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 09:03:01 +01:00
jake
292f9716ff feat(kon): add full transcript provenance — enriched schema, metadata pipeline, error log
Schema enrichment:
- transcripts table: +engine, +model_id, +inference_ms, +sample_rate, +audio_channels,
  +format_mode, +remove_fillers, +british_english, +anti_hallucination
- New error_log table: context, error_code, message, metadata (JSON)
- 5 indices on frequently queried columns

Metadata pipeline:
- TranscriptMetadata struct in kon-core for full provenance capture
- LocalEngine tracks loaded ModelId, returns TimedTranscript with inference_ms
- std::time::Instant timing around transcribe-rs inference calls
- Tauri transcription commands emit inference_ms in event payloads
- InsertTranscriptParams accepts all provenance fields
- log_error() for structured error logging to SQLite

Every transcript is now fully reproducible from its stored metadata.
23 tests passing, clippy clean.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:36:44 +00:00
jake
100ecb4eae feat(kon): add transcription crate — transcribe-rs wrapper, model manager
- LocalEngine wraps transcribe-rs SpeechModel behind Kon's own abstraction
- load_parakeet() and load_whisper() factory functions
- Unified model manager: download with progress callback, check, list, path resolution
- Atomic downloads: .part suffix with rename on completion
- run_inference() encapsulates spawn_blocking threading
- Zero Tauri dependency in this crate (progress via callback, not events)
- transcribe-rs v0.3.2 with onnx + whisper-cpp features
- 4 tests passing, clippy clean

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:39:37 +00:00
jake
9926a42b7a feat(kon): scaffold hybrid modular workspace
- Cargo workspace with 6 domain crates: core, audio, transcription, ai-formatting, storage, cloud-providers
- Minimal Tauri shell (lib.rs + main.rs) with plugin registration
- Svelte 5 frontend copied from Ramble v0.2
- All crates compile as empty stubs
- App identifier: uk.co.corbel.kon

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 20:21:38 +00:00