fix: ChatBubble crash and DeepSeek API compatibility

- Fix ChatBubble to handle non-string content with String() wrapper
- Fix API route to use generateText for non-streaming requests
- Add @ai-sdk/openai-compatible for non-OpenAI providers (DeepSeek, etc.)
- Use Chat Completions API instead of Responses API for compatible providers
- Update ChatBubble tests and fix component exports to kebab-case
- Remove stale PascalCase ChatBubble.tsx file
This commit is contained in:
Max
2026-01-26 16:55:05 +07:00
parent 6b113e0392
commit e9e6fadb1d
544 changed files with 113077 additions and 427 deletions

View File

@@ -0,0 +1,450 @@
---
stepsCompleted:
- step-01-validate-prerequisites.md
- step-02-design-epics.md
- step-03-create-stories.md
- step-04-final-validation.md
inputDocuments:
- file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/prd.md
- file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/architecture.md
- file:///home/maximilienmao/Projects/Test01/_bmad-output/planning-artifacts/ux-design-specification.md
---
# Test01 - Epic Breakdown
## Overview
This document provides the complete epic and story breakdown for Test01, decomposing the requirements from the PRD, UX Design if it exists, and Architecture requirements into implementable stories.
## Requirements Inventory
### Functional Requirements
FR-01: System can detect "Venting" vs. "Insight" intent from initial user input.
FR-02: "Teacher Agent" can generate probing questions to elicit specific missing details based on the user's initial input.
FR-03: "Ghostwriter Agent" can transform the structured interview data into a grammatically correct and structured "Enlightenment" artifact (e.g., Markdown post).
FR-04: Users can "Regenerate" the outcome with specific critique (e.g., "Make it less corporate", "Focus more on the technical solution").
FR-05: System provides a "Fast Track" option to bypass the interview and go straight to generation for advanced users.
FR-06: Users can view a chronological feed of past "Enlightenments" (history).
FR-07: Users can "One-Click Copy" the formatted text to clipboard.
FR-08: Users can delete past entries.
FR-09: Users can edit the generated draft manually before exporting.
FR-10: Users can access the app and view history while offline.
FR-11: Users can complete a full "Venting Session" offline; system queues generation for reconnection.
FR-12: System actively prompts users to "Add to Home Screen" (A2HS) upon meeting engagement criteria.
FR-13: System stores all chat history locally (persistent client-side storage) by default.
FR-14: Users can export their entire history as a JSON/Markdown file.
### NonFunctional Requirements
NFR-01 (Chat Latency): The "Teacher" agent must generate the first follow-up question within < 3 seconds to maintain conversational flow.
NFR-02 (App Load Time): The app must be interactive (Time to Interactive) in < 1.5 seconds on 4G networks.
NFR-03 (Data Sovereignty): User chat logs are stored 100% Client-Side (persistent client-side storage) in the MVP. No user content is sent to the cloud except for the temporary API inference call.
NFR-04 (Inference Privacy): Data sent to the LLM API must be stateless (not used for training).
NFR-05 (Offline Behavior): The app shell and local history must remain accessible in Aeroplane Mode. Active Chat interactions will be unavailable offline as they require live LLM access.
NFR-06 (Data Persistence): Drafts must be auto-saved locally every 2 seconds to prevent data loss.
NFR-07 (Visual Accessibility): Dark Mode is the default. Contrast ratios must meet WCAG AA standards to reduce eye strain for late-night users.
### Additional Requirements
- [Arch] Use Next.js 14+ App Router + ShadCN UI starter template
- [Arch] Implement "Local-First" architecture with Dexie.js (IndexedDB)
- [Arch] Implement Vercel Edge Functions for secure LLM API proxy
- [Arch] Use Zustand for global state management
- [Arch] Implement Service Worker for offline support and sync queue
- [UX] Implement "Morning Mist" theme with Inter (UI) and Merriweather (Content) fonts
- [UX] Implement "Chat" vs "Draft" view split pattern/slide-up sheet
- [UX] Ensure mobile-first responsive design (375px+) with centered container for desktop
- [UX] Adhere to WCAG AA accessibility standards (contrast, focus, zoom)
### FR Coverage Map
FR-01: Epic 1 - Initial intent detection logic in the main chat loop.
FR-02: Epic 1 - Teacher agent logic and prompt engineering for elicitation.
FR-03: Epic 2 - Ghostwriter agent logic and Markdown artifact generation.
FR-04: Epic 2 - Regeneration workflow for draft refinement.
FR-05: Epic 1 - Option to skip straight to generation (Fast Track).
FR-06: Epic 3 - History feed UI and data retrieval.
FR-07: Epic 2 - Copy to clipboard functionality in draft view.
FR-08: Epic 3 - Deletion management in history feed.
FR-09: Epic 2 - Manual editing capabilities for generated drafts.
FR-10: Epic 3 - Offline history access via IndexedDB.
FR-11: Epic 3 - Offline/Online sync queue for venting sessions.
FR-12: Epic 3 - PWA installation prompt logic.
FR-13: Epic 1 - Chat storage infrastructure (Dexie.js).
FR-14: Epic 3 - Data export functionality.
FR-15: Epic 4 (Story 4.1) - Custom API URL configuration.
FR-16: Epic 4 (Story 4.1) - Secure local credential storage.
FR-17: Epic 4 (Story 4.3) - Model selection logic.
FR-18: Epic 4 (Story 4.2) - Connection validation.
FR-19: Epic 4 (Story 4.4) - Provider switching logic.
## Epic List
### Epic 1: "Active Listening" - Core Chat & Teacher Agent
**Goal:** Enable users to start a session, "vent" their raw thoughts, and have the system "Active Listen" (store chat) and "Teach" (probe for details) using a local-first architecture.
**User Outcome:** Users can open the app, chat safely (locally), and get probing questions from the AI.
**FRs covered:** FR-01, FR-02, FR-05, FR-13
**NFRs:** NFR-01, NFR-03, NFR-04
### Epic 2: "The Magic Mirror" - Ghostwriter & Draft Refinement
**Goal:** Transform the structured chat context into a tangible "Enlightenment" artifact (the post) that users can review, refine, and export.
**User Outcome:** Users get a high-quality post from their vent, which they can edit and ultimately copy for publishing.
**FRs covered:** FR-03, FR-04, FR-07, FR-09
**NFRs:** NFR-07 (Visuals), NFR-04
### Epic 3: "My Legacy" - History, Offline Action Replay & PWA Polish
**Goal:** Turn single sessions into a persistent "Journal" of growth, ensuring the app works flawlessly offline and behaves like a native app.
**User Outcome:** Users can view past wins, use the app on the subway (offline), and install it to their home screen.
**FRs covered:** FR-06, FR-08, FR-10, FR-11, FR-12, FR-14
**NFRs:** NFR-02, NFR-05, NFR-06
### Epic 4: "Power User Settings" - BYOD & Configuration
**Goal:** Enable users to bring their own Intelligence (BYOD) by configuring custom API providers, models, and keys, satisfying the "Privacy-First" and "Vendor Independence" requirements.
**User Outcome:** Users can configure and switch between different AI providers with their own API keys, ensuring data privacy and vendor flexibility.
**FRs covered:** FR-15, FR-16, FR-17, FR-18, FR-19
**NFRs:** NFR-03 (Data Sovereignty), NFR-08 (Secure Key Storage)
## Epic 1: "Active Listening" - Core Chat & Teacher Agent
**Goal:** Enable users to start a session, "vent" their raw thoughts, and have the system "Active Listen" (store chat) and "Teach" (probe for details) using a local-first architecture.
### Story 1.1: Local-First Setup & Chat Storage
As a user,
I want my chat sessions to be saved locally on my device,
So that my data is private and accessible offline.
**Acceptance Criteria:**
**Given** a new user visits the app
**When** they load the page
**Then** a Dexie.js database is initialized with the correct schema
**And** no data is sent to the server without explicit action
**Given** the user sends a message
**When** the message is sent
**Then** it is stored in the `chatLogs` table in IndexedDB with a timestamp
**And** is immediately displayed in the UI
**Given** the user reloads the page
**When** the page loads
**Then** the previous chat history is retrieved from IndexedDB and displayed correctly
**And** the session state is restored
**Given** the device is offline
**When** the user opens the app
**Then** the app loads successfully and shows stored history from the local database
### Story 1.2: Chat Interface Implementation
As a user,
I want a clean, familiar chat interface,
So that I can focus on venting without fighting the UI.
**Acceptance Criteria:**
**Given** a user is on the main chat screen
**When** they look at the UI
**Then** they see a "Morning Mist" themed interface with distinct bubbles for User (Right) and AI (Left)
**And** the design matches the "Telegram-style" visual specification
**Given** the user is typing
**When** they press "Send"
**Then** the input field clears and the message appears in the chat
**And** the view scrolls to the bottom
**Given** the user is on a mobile device
**When** they view the chat
**Then** the layout is responsive and all touch targets are at least 44px
**And** the text size is legible (Inter font)
**Given** the AI is processing
**When** the user waits
**Then** a "Teacher is typing..." indicator is visible
**And** the UI remains responsive
### Story 1.3: Teacher Agent Logic & Intent Detection
As a user,
I want the AI to understand if I'm venting or sharing an insight,
So that it responds appropriately.
**Acceptance Criteria:**
**Given** a user sends a first message
**When** the AI processes it
**Then** it classifies the intent as "Venting" or "Insight"
**And** stores this context in the session state
**Given** the intent is "Venting"
**When** the AI responds
**Then** it validates the emotion first
**And** asks a probing question to uncover the underlying lesson
**Given** the AI is generating a response
**When** the request is sent
**Then** it makes a direct client-side request to the configured Provider
**And** the user's stored API key is retrieved from local secure storage
**Given** the API response takes time
**When** the user waits
**Then** the response time is optimized to be under 3 seconds for the first token (if streaming)
### Story 1.4: Fast Track Mode
As a Power User,
I want to bypass the interview questions,
So that I can generate a post immediately if I already have the insight.
**Acceptance Criteria:**
**Given** a user is in the chat
**When** they toggle "Fast Track" or press a specific "Just Draft It" button
**Then** the AI skips the probing phase
**And** proceeds directly to the "Ghostwriter" generation phase (transition to Epic 2 workflow)
**Given** "Fast Track" is active
**When** the user sends their input
**Then** the system interprets it as the final insight
**And** immediately triggers the draft generation
## Epic 2: "The Magic Mirror" - Ghostwriter & Draft Refinement
**Goal:** Transform the structured chat context into a tangible "Enlightenment" artifact (the post) that users can review, refine, and export.
### Story 2.1: Ghostwriter Agent & Markdown Generation
As a user,
I want the system to draft a polished post based on my chat,
So that I can see my raw thoughts transformed into value.
**Acceptance Criteria:**
**Given** the user has completed the interview or used "Fast Track"
**When** the "Ghostwriter" agent is triggered
**Then** it consumes the entire chat history and the "Lesson" context
**And** generates a structured Markdown artifact (Title, Body, Tags)
**Given** the generation is processing
**When** the user waits
**Then** they see a distinct "Drafting" animation (different from "Typing")
**And** the tone of the output matches the "Professional/LinkedIn" persona
### Story 2.2: Draft View UI (The Slide-Up)
As a user,
I want to view the generated draft in a clean, reading-focused interface,
So that I can review it without the distraction of the chat.
**Acceptance Criteria:**
**Given** the draft generation is complete
**When** the result is ready
**Then** a "Sheet" or modal slides up from the bottom
**And** it displays the post in "Medium-style" typography (Merriweather font)
**Given** the draft view is open
**When** the user scrolls
**Then** the reading experience is comfortable with appropriate whitespace
**And** the "Thumbs Up" and "Thumbs Down" actions are sticky or easily accessible
### Story 2.3: Refinement Loop (Regeneration)
As a user,
I want to provide feedback if the draft isn't right,
So that I can get a better version.
**Acceptance Criteria:**
**Given** the user is viewing a draft
**When** they click "Thumbs Down"
**Then** the draft sheet closes and returns to the Chat UI
**And** the AI proactively asks "What should we change?"
**Given** the user provides specific critique (e.g., "Make it shorter")
**When** they send the feedback
**Then** the "Ghostwriter" regenerates the draft respecting the new constraint
**And** the new draft replaces the old one in the Draft View
### Story 2.4: Export & Copy Actions
As a user,
I want to copy the text or save the post,
So that I can publish it on LinkedIn or save it for later.
**Acceptance Criteria:**
**Given** the user likes the draft
**When** they click "Thumbs Up" or "Copy"
**Then** the full Markdown text is copied to the clipboard
**And** a success toast/animation confirms the action
**Given** the draft is finalized
**When** the user saves it
**Then** it is marked as "Completed" in the local database
**And** the user is returned to the Home/History screen
## Epic 3: "My Legacy" - History, Offline Sync & PWA Polish
**Goal:** Turn single sessions into a persistent "Journal" of growth, ensuring the app works flawlessly offline and behaves like a native app.
### Story 3.1: History Feed UI
As a user,
I want to see a list of my past growing moments,
So that I can reflect on my journey.
**Acceptance Criteria:**
**Given** the user is on the Home screen
**When** they view the feed
**Then** they see a chronological list of past "Completed" sessions (Title, Date, Tags)
**And** the list supports lazy loading/pagination for performance
**Given** the user clicks a history card
**When** the card opens
**Then** the full "Enlightenment" artifact allows for reading
**And** the "Copy" action is available
### Story 3.2: Deletion & Management
As a user,
I want to delete old entries,
So that I can control my private data.
**Acceptance Criteria:**
**Given** the user is viewing a past entry
**When** they select "Delete"
**Then** they are prompted with a confirmation dialog (Destructive Action)
**And** the action cannot be undone
**Given** the deletion is confirmed
**When** the action completes
**Then** the entry is permanently removed from IndexedDB
**And** the History Feed updates immediately to remove the item
### Story 3.3: Offline Action Replay
As a user,
I want my actions to be queued when offline,
So that I don't lose work on the subway.
**Acceptance Criteria:**
**Given** the device is offline
**When** the user performs an LLM-dependent action (e.g., Send message, Regenerate draft)
**Then** the action is added to a persistent "Action Queue" in Dexie
**And** the UI shows a subtle "Offline - Queued" indicator
**Given** connection is restored
**When** the app detects the network
**Then** the Sync Manager replays queued actions to the LLM API
**And** the indicator updates to "Processed"
### Story 3.4: PWA Install Prompt & Manifest
As a user,
I want to install the app to my home screen,
So that it feels like a native app.
**Acceptance Criteria:**
**Given** the user visits the web app
**When** the browser parses the site
**Then** it finds a valid `manifest.json` with correct icons, name ("Test01"), and `display: standalone` settings
**Given** the user has engaged with the app (e.g., completed 1 session)
**When** the browser supports it (beforeinstallprompt event)
**Then** a custom "Install App" UI element appears (non-intrusive)
**And** clicking it triggers the native install prompt
**Given** the app is installed
**When** it launches from Home Screen
**Then** it opens without the browser URL bar (Standalone mode)
## Epic 4: "Power User Settings" - BYOD & Configuration
**Goal:** Enable users to bring their own Intelligence (BYOD) by configuring custom API providers, models, and keys, satisfying the "Privacy-First" and "Vendor Independence" requirements.
### Story 4.1: API Provider Configuration UI
As a user,
I want to enter my own API Key and Base URL,
So that I can use my own LLM account (e.g., DeepSeek, OpenAI).
**Acceptance Criteria:**
**Given** the user navigates to "Settings"
**When** they select "AI Provider"
**Then** they see a form to enter: "Base URL" (Default: OpenAI), "API Key", and "Model Name"
**Given** the user enters a key
**When** they save
**Then** the key is stored in `localStorage` with basic encoding (not plain text)
**And** it is NEVER sent to the app backend (Client-Side only)
**Given** the user has saved a provider
**When** they return to chat
**Then** the new settings are active immediately
### Story 4.2: Connection Validation
As a user,
I want to know if my key works,
So that I don't get errors in the middle of a chat.
**Acceptance Criteria:**
**Given** the user enters new credentials
**When** they click "Connect" or "Save"
**Then** the system sends a tiny "Hello" request to the provider
**And** shows "Connected ✅" if successful, or the error message if failed
### Story 4.3: Model Selection & Configuration
As a user,
I want to specify which AI model to use,
So that I can choose between different capabilities (e.g., fast vs. smart).
**Acceptance Criteria:**
**Given** the user is in the API Provider settings
**When** they view the form
**Then** they see a "Model Name" field with examples (e.g., "gpt-4o", "deepseek-chat")
**Given** the user enters a custom model name
**When** they save
**Then** the model name is stored alongside the API key and base URL
**And** all future LLM requests use this model identifier
**Given** the user doesn't specify a model
**When** they save provider settings
**Then** a sensible default is used (e.g., "gpt-3.5-turbo" for OpenAI endpoints)
### Story 4.4: Provider Switching
As a user,
I want to switch between different saved providers,
So that I can use different AI services for different needs.
**Acceptance Criteria:**
**Given** the user has configured multiple providers
**When** they open Settings
**Then** they see a list of saved providers with labels (e.g., "OpenAI GPT-4", "DeepSeek Chat")
**Given** the user selects a different provider
**When** they confirm the switch
**Then** the app immediately uses the new provider for all LLM requests
**And** the active provider is persisted in local storage
**Given** the user starts a new chat session
**When** they send messages
**Then** the currently active provider is used
**And** the provider selection is maintained across page reloads