Commit Graph

32 Commits

Author SHA1 Message Date
aaron 4204806c80 conversations.db, sessions.db: enable WAL, add message index; update backup.sh
Both databases ran with journal_mode=delete — every write rewrote the
rollback journal per transaction. WAL eliminates the journal-rewrite and
lets readers run without blocking writers.

Index on messages(conversation_id, timestamp DESC) is preventive — only
280 rows today, but the access pattern (load conversation history in
order) is exactly what a composite index serves, and we don't want to
re-revisit this when the table grows.

backup.sh updated in the same commit because WAL changes the on-disk
layout: a bare `cp` of just the .db file can miss recently-committed
transactions that still live in the -wal sidecar, and can race with
concurrent writes to produce a torn file. Switched to the SQLite Online
Backup API via python3 -c "...src.backup(dst)..." — same mechanism as
the sqlite3 CLI's `.backup` (which isn't installed on this host),
handles WAL correctly without forcing a checkpoint, and is non-locking
from the writer's perspective. Verified backup integrity_check returns
ok and row counts match.

Note: synchronous=NORMAL was considered but deferred — it's a
per-connection PRAGMA, and applying it correctly requires a connect
helper that wraps every sqlite3.connect() call site in api.py (~14
sites). Out of scope for this commit; tracked as a follow-up. WAL alone
delivers the journal-rewrite elimination and reader/writer concurrency
improvements; the additional fsync reduction from synchronous=NORMAL is
a smaller marginal win on top.

Confirmed via concurrency audit that api.py is the sole writer to both
databases. ingest_conversations.py and dream.py are read-only consumers
of conversations.db; nothing else touches sessions.db.
2026-05-04 03:24:51 +00:00
aaron a27f22ceaf api.py: switch whisper to distil-large-v3, beam_size=1, cpu_threads=4
Three changes to reduce voice-note transcription latency on the VPS:
- Model: large-v3 -> distil-large-v3 (~6x faster, near-identical English
  accuracy; language is already hardcoded "en").
- beam_size: 5 (default) -> 1 (~3-4x faster on clean audio).
- cpu_threads: 8 -> 4 (the box has 8 cores running api, dreamer, watcher,
  nextcloud concurrently; ctranslate2's inter-op pool plus context switching
  makes 4 effectively faster than 8 here).

Combined effect expected ~10-15x over prior config. No accuracy regression
expected for the voice-note use case (English, clean audio, domain terms
already supplied via initial_prompt).
2026-05-04 01:00:32 +00:00
aaron 1101bef226 scripts/encoding.py: Stage 1 dual-implementation consolidation (Track 1 Finding 11)
Consolidates four extract paths and two extract-chunk-embed-write pipelines
into a single shared encoding module. Fixes the embedder lifecycle
divergence between watcher and /api/reindex (no more 200MB reload per
reindex click) and unifies failure tracking so /api/reindex failures now
surface in SettingsPanel "Ingest Health".

New files:
- scripts/encoding.py — extract_text, chunk_text, chunk_and_embed,
  write_embeddings_batch
- scripts/failures.py — record_ingest_failure, resolve_ingest_failure
  (shared by watcher.py and ingest.py)

Refactored:
- scripts/watcher.py — drops local extract/chunk/embed implementations
  and CHUNK_SIZE/CHUNK_OVERLAP/SUPPORTED constants; imports from encoding
  and failures. Now writes ingest_failures row on empty-text-extract
  (was silent return 0).
- scripts/ingest.py — substantial rewrite. Exposes ingest_directory(folder,
  embedder=None) for in-process invocation; CLI back-compat preserved via
  ingest_folder wrapper. Module-level SentenceTransformer load removed.
- scripts/corpus_integrity.py — imports extract_text from encoding;
  extract_text_for_retry function removed.
- scripts/api.py — /api/reindex rewritten with BackgroundTasks (uses
  module-level embedder; no subprocess); new /api/reindex/status endpoint
  reading ~/aaronai/reindex_status.json; /api/corpus/retry imports
  extract_text from encoding; INGEST_SCRIPT constant removed (dead after
  this refactor); 409 reentrance guard prevents double-click stomping.

Behavior changes:
- /api/reindex no longer subprocess.Popens; runs in FastAPI BackgroundTasks
  threadpool, doesn't block API thread.
- /api/reindex no longer reloads SentenceTransformer on each click.
- /api/reindex failures newly write to ingest_failures (visible in
  SettingsPanel "Ingest Health" — badge will jump on first reindex).
- New embeddings rows always have created_at = NOW() (canonical, server-side).
- New embeddings rows always include metadata.folder field (None when not
  derivable).
- /api/reindex returns 409 on second click while a job is running.
- New /api/reindex/status endpoint for polling.

Existing 9,815 NULL created_at rows remain unchanged; backfill is a
separate decision if desired.

199 insertions, 256 deletions across 6 files (codebase shrinks net).

Found by Track 1 inventory 2026-05-02 (Finding 11 / cross-cutting F11).
Pre-commit verification: BackgroundTasks already imported, sys.path
resolves correctly via script-path semantics, static import clean.
2026-05-03 01:40:47 +00:00
aaron 4b520b2bc2 api.py: minor cleanups (Track 1 inventory findings)
- Fix /auth/check endpoint that referenced undefined SESSIONS
  (Phase 1 finding — would NameError 500 on every call). Now uses
  session_exists(token), the live session-validation mechanism
  defined elsewhere in api.py.
- Remove unused DB_PATH ChromaDB-era constant (paired with the
  ChromaDB directory deletion and aaronai-maintenance.service
  removal earlier this session).

Found by Track 1 inventory 2026-05-02. Cross-repo verification of
share_time (third candidate from the original cleanup proposal)
revealed it is working stores-and-returns persistence rather than
dead code; share_time intentionally not modified.

Inventory document edits are committed separately under the docs/
tracking decision.
2026-05-02 23:59:20 +00:00
aaron 7bebd8ae50 api.py: wire up dream_mode setting (Track 1 Finding 9)
The dream_mode setting was defined in DEFAULT_SETTINGS and watched
by update_settings for reschedule, but run_dream_job never read it —
silently-ignored configuration.

Two changes:
1. DEFAULT_SETTINGS["dream_mode"] flipped from "nrem" to "pipeline".
   The default was a latent regression vector: wiring up the setting
   without changing the default would have silently switched all
   default-config users from full-pipeline (current production
   behavior) to NREM-only nightly runs.
2. run_dream_job reads dream_mode at fire-time, validates against
   {"pipeline", "nrem", "early-rem", "late-rem"}, falls back to
   pipeline with a warning on invalid values. Lucid intentionally
   excluded — it is on-demand only by design and remains available
   via CLI and /api/dreamer/run.

Nightly dream production behavior is unchanged for current users
(no settings.json key → default "pipeline" → no flag passed → same
as before). Users can now meaningfully change the nightly mode by
editing settings.json or via the SettingsPanel.

Found by Track 1 inventory 2026-05-02 (Finding 9 / divergence #9).
2026-05-02 23:38:29 +00:00
aaron 6f2d274d5d api.py: remove 50KB truncation from /api/corpus/retry (completes F14)
The F14 fix on 2026-05-01 removed text[:50000] truncation from
watcher.py, ingest.py, and corpus_integrity.py. The retry endpoint
in api.py was missed — clicking 'Retry' on an ingest-failed file
in the SettingsPanel re-introduced the exact truncation pattern
F14 was meant to eliminate.

Found by Track 1 inventory 2026-05-02 (Finding 2 / divergence #2).
2026-05-02 22:56:33 +00:00
aaron 465f2f725b Code review fixes: CV pinning, F1 (excluded_sources), F14 (50KB truncation), F37
- api.py: strip CV pinning workaround (parity violation, see architecture doc)
- dream.py: F1 — retrieve_graphiti() now accepts excluded_sources, over-fetches
  3x and filters in-process. Was silently dropping the parameter; would have
  confounded E3 with broken cross-stage exclusion in Graphiti arm.
- watcher.py + ingest.py: F14 — drop full_text[:50000] truncation. Was
  propagating through entire cascade. Postgres TEXT can hold up to 1GB.
- corpus_integrity.py: F37 — same truncation, third path now clean.

Backups: api.py.bak.*, dream.py.bak.*, watcher.py.bak.*, ingest.py.bak.*,
corpus_integrity.py.bak.* timestamped pre-fix.

Re-cascaded Shop Class as Soulcraft (only already-cascaded source affected
by F14, 414KB).
2026-05-01 02:26:37 +00:00
aaron 74e2c34f43 corpus integrity: ingest_failures tracking in watcher, reconciliation script, corpus status/retry/reconcile endpoints 2026-04-30 21:54:39 +00:00
aaron 1cf26df450 api.py: return error_type=transcription_failed on Whisper crash, frontend retry logic can now distinguish from network failures 2026-04-30 17:45:47 +00:00
aaron d91a5675ff capture: public SSE endpoint for transcription completion events 2026-04-29 18:00:54 +00:00
aaron c42d898504 emit capture_saved SSE event when async transcription completes 2026-04-29 17:58:01 +00:00
aaron a05fcec882 async voice transcription — return immediately, whisper runs in background 2026-04-29 17:48:22 +00:00
aaron eb7cf3be10 upgrade whisper small -> large-v3, bump cpu_threads to 8 2026-04-29 17:35:03 +00:00
aaron 3f6c435be4 add client_time to chat context — user-supplied, not logged 2026-04-29 17:26:03 +00:00
aaron 21557790d9 capture: return error_type on transcription failure instead of HTTP 500 2026-04-29 17:04:56 +00:00
aaron 794e0aeddd update whisper prompt: add BirdAI stack terms, remove stale ChromaDB 2026-04-29 16:47:30 +00:00
aaron d271e17929 add sourcing constraint to system prompt, close hallucination gap 2026-04-29 16:37:39 +00:00
aaron 037d747573 chore: archive deprecated chromadb and migration scripts 2026-04-28 00:15:46 +00:00
aaron 6776637178 Remove hardcoded PG password fallbacks — require PG_DSN env var in all scripts 2026-04-27 05:16:37 +00:00
aaron a1f5c1049a Fix dreamer status display, watcher excludes Media/, remove NVM debt item 2026-04-27 05:08:01 +00:00
aaron d3239aba17 Image capture — extend /api/capture for image+voice, Claude vision description, Media/ WebDAV, watcher excludes Media/ 2026-04-27 04:28:31 +00:00
aaron 7af246ac01 APScheduler — replace systemd timers, in-process dream and ingest scheduling 2026-04-27 03:04:33 +00:00
aaron 9b312d936f Add SSE endpoint and dream notify — /api/events and /api/events/notify 2026-04-27 02:20:50 +00:00
aaron 9088b5643d Add /api/dreamer/status and /api/dreamer/run endpoints 2026-04-27 01:27:09 +00:00
aaron a07de922df Add /api/capture and /api/captures endpoints — auth-free, WebDAV delivery to Journal/Captures/ 2026-04-26 22:39:55 +00:00
aaron f78b83042b Migrate to pgvector — remove ChromaDB from api.py, ingest scripts, dream.py 2026-04-26 21:16:04 +00:00
aaron d2eed98906 Pre-pgvector migration checkpoint — upsert, allow_replace_deleted, maintenance timer 2026-04-26 20:19:49 +00:00
aaron fd76426f38 Persist sessions to SQLite — survive service restarts 2026-04-26 16:16:30 +00:00
aaron 050fe4669b Add Whisper small model — /api/transcribe endpoint, VAD filter, domain vocabulary prompt 2026-04-26 15:25:22 +00:00
aaron 17e06b1e70 Add session-based auth — replace Basic Auth with httpOnly cookie, 30-day expiry 2026-04-26 03:38:35 +00:00
aaron 187d31eaff Fix watcher status indicator — write status file every 5s, API reads it directly 2026-04-25 16:58:19 +00:00
aaron 22ef40bbaa Initial commit - Aaron AI v1 2026-04-25 02:05:42 +00:00