Skip to content

Integrate backend generation, live views, and Tome Home#46

Merged
zeapsu merged 12 commits into
mainfrom
integrate/spencer-foundations-tome-home
May 27, 2026
Merged

Integrate backend generation, live views, and Tome Home#46
zeapsu merged 12 commits into
mainfrom
integrate/spencer-foundations-tome-home

Conversation

@zeapsu

@zeapsu zeapsu commented May 26, 2026

Copy link
Copy Markdown
Owner

Summary

  • cherry-picks Spencer's backend generation/audio/session work and frontend live-view foundations while preserving commit authorship
  • adds backend polish for session limit clamping, generation/audio endpoint tests, and legacy DB schema compatibility
  • reconciles Tome Home as the default visual/product surface on top of the live Sage views and chat handoff

Test Plan

  • cd backend && uv run --group dev pytest
  • cd frontend && npm run build

Notes

spencerwozniak and others added 12 commits May 26, 2026 15:52
Adds POST /api/generate/{flashcards,quiz,report} on a new router. Each
endpoint pulls excerpts from the knowledge base (semantic search when a
topic is provided, random sample otherwise — optionally scoped to a
tome), prompts the configured LLM with response_format=json_object on
OpenAI-compatible providers, and validates the parsed payload before
responding. An empty KB returns 400 with a hint to upload documents.

Also exposes an llm_text(...) helper used by the audio endpoint for
plain-text completions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POST /api/generate/audio retrieves KB context (or randomly samples it),
prompts the configured provider for a spoken-style narration script,
and segments it into sentence-level chunks with word-rate-estimated
timings for transcript scroll-lock.

When OPENAI_API_KEY is configured, the script is also synthesized to
MP3 via OpenAI's tts-1 voice (default: alloy) and cached to disk; the
response includes audio_url. Without a key, audio_url is null and the
frontend falls back to the browser's SpeechSynthesis voice.

GET /api/audio/{audio_id}.mp3 streams the cached file with a regex
guard against directory traversal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Adds KnowledgeStore.list_sessions(): one SQL pass joining sessions,
  messages, and tomes that returns derived title (first user message),
  tome name, message count, and last activity timestamp. Ordered by
  most-recent activity.
- Adds GET /api/chat/sessions?limit=<n> as a thin wrapper for the
  History panel.
- Extends the chat SYSTEM_PROMPT with an "App capabilities you should
  know about" section so the model can describe its own routed
  features (quiz / flashcards / audio / report / history / tomes /
  knowledge base / upload) accurately when the user asks "what can
  you do?", and skips replying when the frontend has already routed
  away.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds typed helpers covering every new backend surface:

- Knowledge base: listDocuments, getDocument, deleteDocument plus
  DocumentSummary / DocumentDetail.
- Generation: generateFlashcards, generateQuiz, generateReport,
  generateAudio with shared GenerateOpts and GeneratedSource.
- Audio: AudioGeneration / AudioSegment plus resolveAudioUrl() that
  joins a relative audio path against the active API base (so it
  works from both the Next.js dev server and the Tauri sidecar).
- History: listChatSessions and ChatSessionSummary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two new components reachable from the round button next to the
command-bar pill:

- UploadModal: paste / drag-drop / file-picker ingest of text-like
  files (.txt, .md, .markdown, .rst, .csv, .json, .log). Backend
  dedupes by content hash so re-uploads are safe.
- KnowledgeBaseWidget: two-pane list+detail browser over every
  ingested document. Lists titles with doc_type/source/timestamp,
  shows chunk count, content hash, linked tomes, and a 200-char
  preview. Per-row delete. An "Add document" button in the header
  opens UploadModal and refreshes the list on close.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the hard-coded SAMPLE_* placeholders that the original
FlashcardWidget / QuizWidget / AudioPlayerWidget / ReportViewWidget
rendered with real, KB-grounded views that POST to the new
/api/generate/* endpoints.

Each view shares the same shape: loading card, error card with retry,
content card, and a "Grounded in: ..." source footer listing the
distinct source documents.

AudioPlayerWidget gains two new props — audioUrl and script — and
switches between two playback modes:

- <audio> element when the backend synthesized an MP3 (OpenAI TTS).
  Timeupdate events drive currentTime; a Download link appears.
- SpeechSynthesis fallback when no audio_url. Seek bar restarts
  speech from the nearest segment; a "Browser TTS" badge appears
  in place of Download.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ChatWidget now POSTs to /api/chat via sendChat(), tracks sessionId
  across turns, surfaces backend errors inline as assistant-style
  messages, and accepts an initialQuery prop so the first command-bar
  prompt seeds the conversation.
- The verb router used to live only in the command bar. detectView()
  is now lifted to page.tsx and shared with ChatWidget through an
  onCommand callback, so typing "make flashcards on attention" from
  inside the chat view routes correctly instead of being answered as
  prose. Supported verbs: quiz / test / flashcards / audio /
  podcast / listen / report / study guide / summary / history /
  tome / collection / library / knowledge / kb / docs.
- CommandBar gets a circular knowledge-base button (icon: library_add)
  to the right of the pill that opens the KB browser; the model badge
  is now driven by getRuntimeConfig() instead of being hard-coded.
- page.tsx threads generationPrompt through the routed views so the
  user's full command text becomes the topic for /api/generate/*.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the SAMPLE_HISTORY array with a fetch against
GET /api/chat/sessions. Filters out stub sessions (no user message
or zero messages), derives a row title from the first user message
(first line, 90 char cap), and groups rows into Today / Yesterday /
Earlier this week / Older buckets using a shared formatTimestamp
helper that handles both `datetime('now')` and ISO timestamp formats.

Search input filters by title or tome name client-side. Loading,
error, and empty states are inline. The filter pills (chat / quiz /
flashcard / etc.) are removed for now — only chat rows exist until
the other generations gain server-side persistence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants