feat(frontend): API client, KB UI, live views, chat routing, history panel#43
feat(frontend): API client, KB UI, live views, chat routing, history panel#43spencerwozniak wants to merge 8 commits into
Conversation
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>
|
Thanks for breaking this out. There is a lot of useful UI/API work here, especially the API client and the feature views for KB, quiz, flashcards, report, audio, and history. I reviewed this in an isolated checkout. Checks:
I would hold this before merge for a few runtime/product issues. Main blockers:
Running against an older Sage DB can hit: Fresh DBs pass, but existing user DBs need a schema-hardening/migration path before this lands. I’d suggest checking existing columns with
Suggested fixes:
The PR routes Other follow-ups:
One product-direction note: Sage is moving toward a calm default Tome Home surface with capability chips, while the expanded dashboard remains secondary. A lot of this PR is still useful, but I think we should adapt the entry points into that Tome Home flow rather than relying on broad keyword hijacking from the command bar. Recommended path:
Great Sage appreciates how much implementation is here. I just want to make sure it lands in a way that is safe for existing local data and aligned with the calmer Tome Home direction. |
|
Heads up: we’re going to implement the redesigned Tome Home on our Tome Dashboard/Home branch, and that work will touch frontend/src/app/page.tsx. It should be mostly product/surface-level work separate from this PR’s backend/API-client/live-view wiring, but I wanted to flag the overlap early so we can avoid unnecessary merge-conflict pain. Great Sage housekeeping ✨ |
|
Thanks for splitting out the frontend integration work. PR #46 has now been merged into main as 53b83e0 and cherry-picked/integrated the API client, KB UI, live views, chat routing, and history-panel work from this PR, with the Tome Home reconciliation and extra polish/tests on top. I'm closing this as superseded by #46 so the open PR list reflects the current mainline state. |
Summary
Split out of #37 — frontend wiring that consumes the backend endpoints. Includes the backend commits as dependencies so this PR is self-mergeable against
main; once #42 (backend-only) merges first, those commits will drop out of this diff on rebase.frontend/src/lib/sage-api.ts) — typed helpers for knowledge, generation, audio, history;resolveAudioUrl()joins relative audio paths against the active API base for both Next.js dev and Tauri sidecar.UploadModal(paste / drag-drop / file-picker for.txt|.md|.markdown|.rst|.csv|.json|.log, backend dedupes by content hash) andKnowledgeBaseWidget(two-pane browser with chunk count / hash / linked tomes / preview / per-row delete).FlashcardsView,QuizView,AudioView,ReportViewreplace the SAMPLE_* placeholders and POST to/api/generate/*. Shared loading / error+retry / content / sources-footer shape.AudioPlayerWidgetswitches between<audio>element (when MP3 available) and SpeechSynthesis fallback.ChatWidgetPOSTs to/api/chat, trackssessionId, surfaces errors inline.detectView()lifted topage.tsxand shared withChatWidgetso verbs ("make flashcards on attention", etc.) route from inside the chat view. CommandBar gets a circular KB button.GET /api/chat/sessions; client-side search, Today/Yesterday/Earlier-this-week/Older bucketing, stub-session filtering.Test plan
OPENAI_API_KEY(MP3 mode vs. browser TTS fallback)