Commit Graph

427 Commits

Author SHA1 Message Date
bvandeusen fda6a7acc1 feat(tts): wire useStreamingTts into ChatView 2026-04-03 13:09:52 -04:00
bvandeusen 7ffd412603 feat(tts): add useStreamingTts composable for sentence-level streaming 2026-04-03 12:54:05 -04:00
bvandeusen b92c6b1487 docs: streaming TTS implementation plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:43:28 -04:00
bvandeusen b8cd2e5ed7 docs: streaming TTS design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 12:16:56 -04:00
bvandeusen ef55bcb560 feat(llm): adaptive num_ctx tiers + fix KV cache priming num_ctx mismatch
Adds pick_num_ctx() which selects the smallest context window tier
(8192, 16384, 32768) that fits the current messages with 25% headroom,
capped at OLLAMA_NUM_CTX. Threads num_ctx through generation_task.py so
every chat request uses the computed tier rather than a fixed 16384.

Fixes a critical cache miss bug: KV cache priming in app.py and
settings.py was sending requests without num_ctx, so Ollama sized the
cache at its model default (different from the 16384 real requests used),
forcing a full model reload on the first real user message. Both priming
sites now call pick_num_ctx() and pass the matching value.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 11:47:39 -04:00
bvandeusen a6888953dc perf(prompts): trim tool schema descriptions to reduce prompt token count
Removes verbose redundant text from tool descriptions and system prompt
guidance: multi-line recurrence_rule JSON examples, CAPS warnings that
duplicate system prompt instructions, and wordy descriptions that don't
add model understanding.

Saves ~990 tokens per request (~17% reduction, 5,639 → ~4,650 tokens),
reducing prefill time on cache misses and lowering KV memory pressure.
No functional changes — parameter names, types, enums, and required
fields are unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 11:01:05 -04:00
bvandeusen c9065c4481 perf(settings): prime KV cache when user changes chat model
When a user saves a new default_model in Settings, fire a background
cache-prime request so the first message with the new model is fast
rather than paying the full cold-start prefill cost.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 10:51:17 -04:00
bvandeusen 4792e6459b perf(startup): prime Ollama KV cache with system prompt on warm-up
After loading each user's chat model into VRAM, send a minimal chat
request with the real system prompt (num_predict=1) to populate the
KV cache. The first real user message then only needs to process its
own tokens rather than the full ~5,600-token system prompt, reducing
cold-start TTFT from ~25s to <1s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 10:47:59 -04:00
bvandeusen 3bd0dc6879 feat(settings): add background model picker with KV cache performance warning
Exposes OLLAMA_BACKGROUND_MODEL as a per-user setting in General settings,
alongside the Chat Model selector. Includes an inline warning when the same
model is selected for both, explaining the KV cache performance impact.

All background task callers (title generation, tag suggestions, project
summaries, RSS classification) now read background_model from user settings,
falling back to OLLAMA_BACKGROUND_MODEL env var.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 10:44:08 -04:00
bvandeusen fa38978745 fix(lint): remove unused model variable and get_setting import in chat.py
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 10:17:35 -04:00
bvandeusen 750a91898a perf(llm): route background tasks to dedicated model to preserve KV cache
Background tasks (title generation, tag suggestions, project summaries,
RSS classification) were using qwen3:8b and wiping its KV cache after
every response, preventing prefix cache hits on subsequent user messages.

Adds OLLAMA_BACKGROUND_MODEL (default: qwen2.5:0.5b) config var and
routes all background LLM calls to it, keeping qwen3:8b's KV cache
warm between user messages for consistent sub-second TTFT.

Also adds infinite scroll to KnowledgeView (replaces load-more button)
and bakes spaCy en_core_web_sm into the Docker image to eliminate the
pip install on every startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 01:33:54 -04:00
bvandeusen 888b736ecd feat(weather): default to today only, add days parameter for multi-day requests
get_weather now returns 1 day by default (today) instead of a full 7-day
forecast. A new optional `days` parameter (1–8) lets the model request
more days when the user explicitly asks for a weekly forecast or specific
date range. Tool description updated to guide the model accordingly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:55:40 -04:00
bvandeusen a473f6e039 fix: minichat markdown rendering and weather temperature unit preference
- KnowledgeView minichat: render assistant messages through renderMarkdown
  so headers, bold, lists etc. display correctly instead of raw markdown
- get_weather tool: read user's temp_unit from briefing_config and convert
  temperatures to °F when preferred; also include temp_unit in the
  returned payload so the model can label values correctly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:52:54 -04:00
bvandeusen 07f4956550 fix: center minichat widget and fix weather tool success status
- KnowledgeView minichat: add margin-inline: auto so the widget centers
  within the content area when max-width is reached on wide screens
- weather get_weather tool: return success: true on both the arbitrary
  location path and the cached locations path so ToolCallCard shows
  the correct success state instead of always flagging as error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:50:42 -04:00
bvandeusen b4be1f0799 perf(llm): move retrieval context to user turn for stable system prompt
RAG notes, RSS news, current note, URL content, and briefing articles
are now prepended to the user message rather than appended to the system
message. The system message now contains only stable content (persona,
tool guidance, date, profile, workspace, history summary), making its
token sequence identical across consecutive requests and allowing
Ollama's KV prefix cache to fire reliably every time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:44:17 -04:00
bvandeusen 36634919cc fix(home): center quick chat widget and constrain width
Chat section and inline response now have max-width 720px centered
within the page, and quick action chips are centered. Prevents the
widget from stretching the full 1200px content width on wide screens.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 23:26:53 -04:00
bvandeusen 8a10eb9dbd feat(generation): add conditional thinking classifier
Routes simple/conversational messages to think=false automatically,
even when the user has thinking enabled. Patterns checked: word count
thresholds, complexity keywords, code blocks, skip patterns for greetings
and simple CRUD. Workspace mode (think=true from frontend) still benefits
from the classifier on short messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 23:19:02 -04:00
bvandeusen 2422946b4f perf: remove model-load polling before generation
wait_for_model_loaded() polled /api/ps for up to 180s waiting for the
model to appear as loaded. But Ollama lazy-loads models on the first
/api/chat request, so the poll will never succeed — it just blocks for
the full 180s after every Ollama restart before proceeding.

Removed the wait entirely. Ollama handles on-demand loading correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 22:36:31 -04:00
bvandeusen b416fec292 perf: reduce OLLAMA_NUM_CTX default from 65536 to 16384
65536 was causing Ollama to allocate a ~50GB KV cache, spilling 77% of
the model to CPU RAM and making prefill extremely slow (35-125s TTFT).

16384 covers 30+ message conversations comfortably while keeping the KV
cache small enough to stay on GPU.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:49:55 -04:00
bvandeusen d20320b664 fix(fable-mcp): raise read timeout to 300s for cold model load
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 19:45:51 -04:00
bvandeusen c3665ddda5 perf: restructure system prompt for Ollama KV-cache prefix reuse
Move static content (persona + tool guidance) to a fixed prefix and
append all dynamic content (date, timezone, profile, entities) as a tail.

Ollama prefix caching requires byte-for-byte token match from the start
of the prompt. Previously, Today's date + user profile were embedded
mid-prompt, invalidating the cache on every request/day and causing
~20s TTFT regardless of model warmth.

With this change the static prefix (~5500 tokens) should be cached
after the first request each session, reducing TTFT to ~2-5s for the
~200-token dynamic tail only.

Also removed inline user_timezone from tool_lines (timezone is now
stated once in the dynamic tail, which the model reads).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:29:26 -04:00
bvandeusen a7160772bf fix(fable-mcp): fix SSE parser and add stream retry for race condition
- Parse multi-line SSE format correctly (event: on separate line, chunk in data.chunk)
- Retry stream GET up to 10x with 300ms backoff when 404 (buffer not ready yet)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 16:10:12 -04:00
bvandeusen 746b21fa4c fix(fable-mcp): fix send_message to use correct two-step API flow
POST .../messages to start generation, then stream from .../generation/stream.
The previous implementation used a non-existent /stream endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 16:02:44 -04:00
bvandeusen 8a54daf3c9 fix(fable-mcp): fix streaming error handling and bump timeout for SSE
- stream_get now reads error responses before calling _raise_for_status
  (httpx raises on .json()/.text access inside an unread stream context)
- Raise read timeout to 120s (was 30s) so generation streams don't timeout
- Document "generation" as a valid category for fable_get_app_logs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:46:39 -04:00
bvandeusen 25d448f896 feat: add generation metrics to logs (think, rounds, tokens)
Log think flag, round count, prompt/output token counts per generation.
Change log category from 'usage' to 'generation' for clean MCP filtering.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:14:41 -04:00
bvandeusen 3e42992f67 fix: stop CI from filling runner disk
Three sources of unbounded growth removed:
- Drop cache-from/cache-to registry: on a persistent self-hosted runner the
  local BuildKit layer cache already provides between-run reuse; the registry
  cache was redundant and pushed ~2 GB of torch layers on every build
- Switch docker system prune -f → -af so old :SHA-tagged images are removed,
  not just dangling ones (-f alone never touched named tags)
- Add docker builder prune --keep-storage 5g to bound the local BuildKit
  cache; pip mount cache (torch etc.) is recently-used so survives, stale
  intermediate layers are evicted first

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 13:44:09 -04:00
bvandeusen 749a60b9fd feat: interactive checkboxes in list note viewer
- renderMarkdown() accepts interactiveCheckboxes option: removes disabled=""
  and stamps data-task-index on each checkbox in the marked HTML output
- NoteViewerView detects list notes by body content (- [ ] / - [x] pattern)
  and passes interactiveCheckboxes: true when rendering
- onBodyChange() handles checkbox change events: toggles the matching line
  in the body, optimistically updates the store, then PATCHes the note
- prose.css adds .prose--checklist rules for marked output: no bullet,
  flex row, accent-color, line-through on checked items via :has()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 09:31:37 -04:00
bvandeusen aec7a910f0 fix: disable provenance attestation for Forgejo registry compatibility
build-push-action@v7 generates OCI attestation manifests by default.
Forgejo's registry doesn't support OCI image index format with attestations,
causing the push to fail with "unknown". provenance: false disables this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 23:11:29 -04:00
bvandeusen 9549eb85bc feat: live card refresh, list checkboxes, minichat width cap
KnowledgeView:
- Watch streamingToolCalls; call fetchItems+fetchTags on create/update
  note or task so the card grid reflects changes made via the minichat
- Cap minichat to max-width: var(--page-max-width) so it matches chat column width

WorkspaceView + WorkspaceNoteEditor:
- Expose reload() from WorkspaceNoteEditor via defineExpose
- Call noteEditorRef.reload() alongside taskPanelRef.reload() when
  create_note/update_note tools succeed in the SSE watcher

KnowledgeView list cards:
- Backend: parse markdown task list into list_items [{text, checked}] + body
- Card renders up to 6 items with real checkboxes; toggleListItem()
  does an optimistic update then PATCHes /api/notes/:id
- Progress bar kept below items; "+N more" shown when list is long

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 21:30:28 -04:00
bvandeusen 23a7ed7822 refactor: DRY layout bounds via --page-max-width and --sidebar-width CSS vars
Add --page-max-width (1200px), --page-padding-x (1rem), and --sidebar-width (260px)
to theme.css so all views share a single source of truth.

- HomeView: 1100px → var(--page-max-width) (aligns with all other views)
- NotesListView, TasksListView, ProjectListView, ProjectView, CalendarView: var(--page-max-width)
- ChatView: sidebar + context sidebar → var(--sidebar-width); inner message/input
  column max-widths → var(--page-max-width)
- KnowledgeView: filter panel 180px → var(--sidebar-width); minichat left offset updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:56:33 -04:00
bvandeusen d5771a3d5c fix: remove view max-width constraints; widen graph panel
- Drop max-width from .knowledge-root so the graph panel can use the full
  viewport width without hitting a cap
- Drop max-width from .chat-page (message bubbles are already self-constraining)
- Increase normal graph panel width 420px → 500px
- Increase expanded width to min(960px, 60vw) so it scales with the viewport
  and updates minichat right-offset to match

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 20:39:21 -04:00
bvandeusen c95afa4558 fix: install CPU-only torch to avoid 2GB+ CUDA packages on CI runner
kokoro and transformers pull full nvidia CUDA wheels by default (~2 GB),
exhausting the runner disk. Pre-installing torch from the CPU wheel index
satisfies the dependency and prevents pip from selecting the CUDA variant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 19:02:09 -04:00
bvandeusen 8140bc022c fix: resolve ENOSPC on CI by using BuildKit pip cache and registry layer cache
- Add `# syntax=docker/dockerfile:1` to enable BuildKit cache mounts
- Replace `--no-cache-dir` pip installs with `--mount=type=cache,target=/root/.cache/pip`
  so torch/CUDA wheels are reused across builds instead of re-downloaded every run
- Add `docker system prune -f` step before build to free dangling image/layer space
- Add `cache-from`/`cache-to` pointing to `:cache` tag so unchanged layers
  (including the heavy voice-deps layer) are pulled from registry instead of rebuilt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:23:59 -04:00
bvandeusen 0d12d01115 feat: constrain Chat/Knowledge width + persistent expandable graph panel
Layout:
- Chat and Knowledge views now max out at 1600px and center on wide
  screens, consistent with the rest of the app; app-content overflow
  is set to hidden for both so they manage their own scroll

Graph panel (Knowledge):
- Open/closed state persisted to localStorage (fa_knowledge_graph_open)
  — stays open across navigation and page refreshes
- Expanded state persisted (fa_knowledge_graph_expanded): chevron button
  in the panel header toggles between 420px (normal) and 700px (expanded)
- Minichat right offset follows the panel width with a matching transition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:01:18 -04:00
bvandeusen d96895c276 fix: briefing refresh — weather not populated + workspace conv bleed
Two bugs:

1. Manual 'Refresh' button didn't refresh weather/RSS before compiling.
   The daily scheduler calls refresh_all_feeds + refresh_location_cache
   before run_compilation; the manual trigger route called run_compilation
   directly, reading a stale cache. Manual trigger now mirrors the
   scheduler: refresh feeds and all configured weather locations first.

2. Navigating to WorkspaceView during a long briefing compilation caused
   the workspace chat to show the briefing content. triggerNow awaits
   ~30-60s; on completion it called loadAll() → chatStore.fetchConversation
   which overwrote the workspace's currentConversation in the shared store.
   Fixed with a _mounted flag — all post-async state writes in BriefingView
   are now guarded so they no-op if the component has been unmounted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:46:53 -04:00
bvandeusen 145c18d8a3 refactor: DRY calendar additions + define --color-surface in theme
- Define --color-surface in theme.css (light: #f0f0f8, dark: #1a1b22)
  — was used across 10+ components with no-op fallbacks; now properly
  defined alongside --color-bg and --color-bg-card
- Extract shared date formatters into utils/dateFormat.ts:
  fmtTime, fmtDateTime, fmtRelativeDateTime, fmtDayLabel, fmtCompact
- Replace duplicate inline formatters in CalendarView, HomeView
  (formatUpcomingTime), and KnowledgeView (formatEventDate)
- CalendarView: replace hardcoded rgba(99,102,241,0.4) hover colour
  with color-mix(in srgb, var(--color-primary) 40%, transparent);
  fix --color-input-bg fallback to use var(--color-bg); remove
  hardcoded hex fallbacks now that --color-surface is defined

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:27:23 -04:00
bvandeusen f96013a4bc feat: calendar event popover + upcoming events strip
B — Event popover: clicking a calendar event shows a compact overlay with
    full details (title, time range, location, description, color accent)
    and Edit/Close actions; positioned relative to the click, closes on
    outside click; Edit opens the existing EventSlideOver

C — Upcoming strip: scrollable section below the calendar showing the
    next 4 weeks of events grouped by day (Today/Tomorrow/date label),
    each card with color accent bar, title, time, location, description
    snippet; clicking a card opens EventSlideOver for edit

Both features stay in sync with create/update/delete operations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:10:16 -04:00
bvandeusen 30981a3121 feat: persist listen mode across views via localStorage
Extract listen mode into a shared useListenMode() composable backed by
localStorage ('fa_listen_mode'). ChatView and BriefingView both use it,
so toggling auto-read on in one view keeps it on after navigation or
page refresh — no need to re-enable it each visit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 11:53:03 -04:00
bvandeusen 71b8c5965c fix: briefing discuss button — full article content + scroll + optimistic UI
Three bugs in discussArticle():
- Scroll selector was '.briefing-chat' (doesn't exist) → '.briefing-center';
  the panel never scrolled into view so the response was invisible until refresh
- Only 300-char snippet was sent to the LLM; now passes the full stored
  content (up to 2000 chars) from the backend
- User message wasn't shown until streaming ended; now added optimistically
  to messages[] immediately on click so it appears straight away

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 11:46:23 -04:00
bvandeusen 5924e565b1 fix: floating mini-chat overlay style + weather precip fallback
- KnowledgeView mini-chat: replace harsh border-top with upward box-shadow
  and rounded top corners (16px); remove padding-bottom from content area
  so widget truly overlays cards without pushing layout; add collapse
  toggle (chevron) that hides messages without closing the conversation
- WeatherCard: show precip_mm as fallback when precipitation_probability_max
  is null but actual rainfall is expected (Open-Meteo omits probability for
  some forecast days even when rain is shown)
- Pass precip_mm through weather service → frontend type definitions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 09:04:47 -04:00
bvandeusen a0620c4949 fix: knowledge view — chat input styling, autofocus, graph component (no iframe)
- Mini-chat input bar now uses shared --color-input-bar-* CSS variables and
  the same chat-input-bar pill pattern as all other chat interfaces
- chatInputEl focused on mount (autofocus on page load)
- Graph panel: replaced iframe (blocked by X-Frame-Options: DENY) with
  inline GraphView component; CSS :deep override constrains its height
  to fill the panel instead of 100vh

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:49:02 -04:00
bvandeusen 95056d5be7 fix: rename Note.metadata → entity_meta (reserved by SQLAlchemy Declarative API)
SQLAlchemy reserves 'metadata' as a class attribute on declarative models.
Renamed to 'entity_meta' with explicit column name 'metadata' so the DB
column is unchanged but the Python attribute no longer conflicts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:13:47 -04:00
bvandeusen 80f30b705d feat: Knowledge view + entity types (People, Places, Lists)
Data model:
- Migration 0036: adds note_type TEXT (default 'note') and metadata JSONB
  to the notes table; index on note_type
- Note model: entity_type property, note_type/metadata in to_dict()
- create_note() accepts note_type and metadata params

Backend:
- /api/knowledge — unified paginated endpoint: type/tag/sort/q filters,
  semantic search via embeddings, excludes tasks
- /api/knowledge/tags — distinct tags across knowledge objects
- New LLM tools: create_person, create_place, create_list, add_to_list,
  clear_checked_items — all wired into execute_tool()
- People and places auto-injected as compact summary into LLM system prompt

Frontend:
- KnowledgeView replaces HomeView at /; left filter panel (type+tag),
  toolbar (search, sort, graph toggle), card grid with type-aware cards
  (indigo=note, emerald=person, amber=place, sky=list), load-more pagination
- Today bar: upcoming events, overdue task count, Briefing/Chat links
- Floating mini-chat sticky to bottom: creates/continues a conversation
  inline, message history expands upward, close button ends session
- Graph panel: toggles as a 420px right panel at full viewport width
- AppHeader: Knowledge, Chat, Briefing, Calendar, Tasks, Projects
- Router: / → KnowledgeView; /knowledge redirect; HomeView import removed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:01:03 -04:00
bvandeusen 425d307180 feat: automatic Kokoro offline mode + daily update check
- On first load: model runs online (downloads .pt files), then stores the
  current HF commit SHA to /data/kokoro_commit_hash.txt and switches the
  process to offline mode (HF_HUB_OFFLINE) for all future requests
- On subsequent restarts: presence of the commit file triggers offline mode
  before the pipeline loads, skipping all HuggingFace network validation
- Daily at 03:00 UTC: scheduler temporarily lifts offline mode, fetches the
  latest commit SHA from HF, and only reloads the pipeline if the model has
  actually changed — then restores offline mode
- Removed HF_HUB_OFFLINE from docker-compose.yml; behaviour is now automatic
  and not a hoster/user concern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:06:13 -04:00
bvandeusen f2dd25737a feat: add HF_HUB_OFFLINE env var to skip HuggingFace cache validation on startup
Once Kokoro voice .pt files are cached locally, setting HF_HUB_OFFLINE=1
prevents HEAD requests to HuggingFace on each restart, making voice pre-warming
fully offline and faster.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:00:50 -04:00
bvandeusen 882ea176b2 fix: briefing discuss prompt — suppress research tool to prevent full research note
Explicitly instruct the LLM to respond conversationally and not use any
research/search tools when summarizing a shared article excerpt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:54:18 -04:00
bvandeusen baeb0b14e5 feat: listen mode + volume knob in chat; briefing discuss auto-send; fix LLM proactive note search
- ChatView: listen mode toggle (auto-reads new responses via TTS), volume popup
  with range slider persisted per-device in localStorage via GainNode
- useVoiceAudio: shared module-level _volume ref with localStorage persistence,
  GainNode for volume control, exported setVoiceVolume()
- tts.py: pre-warm all Kokoro voices at pipeline load to eliminate HuggingFace
  HEAD requests at synthesis time (reduces TTS latency)
- BriefingView: discuss article button now auto-sends instead of just filling input;
  prompt capped to 15 sentences; send() accepts optional overrideText
- llm.py: instruct LLM not to proactively search notes or comment on note absence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:52:11 -04:00
bvandeusen ab397e78f3 fix: briefing TTS now uses saved voice/speed/blend settings
synthesiseSpeech() called without explicit params now omits voice/speed/
blend from the request body. The backend detects this and auto-loads all
three from the user's saved settings (voice_tts_voice, voice_tts_speed,
voice_tts_blend), so briefing listen mode respects the voice the user
configured in Settings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:46:52 -04:00
bvandeusen ea23f16bd7 feat: weather card — precip probability %, condition text, unit-aware wind
- Fetch precipitation_probability_max from Open-Meteo (replaces precip_sum
  in the card display — probability is more useful at a glance than mm)
- Show WMO condition description text on each forecast day
- Convert wind speed to mph when temp unit is F; pass wind_unit in response
- Display 💧 X% chance of rain; 💨 X mph/km/h wind per day

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:02:36 -04:00
bvandeusen c1fcb1e287 feat: discuss article in briefing chat via 💬 button on news cards
Clicking 💬 on a news card in the briefing panel pre-fills the briefing
chat input with the article title, snippet, and source so the user can
ask the briefing LLM to summarize or discuss it directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 12:57:25 -04:00