Commit graph

51 commits

Author SHA1 Message Date
Vadym Samoilenko
bc4138f332 Final pieces: decorators on LLM routes, usage self-service, billing page, WS events
Backend:
- @active_required + @with_user_context applied to all LLM-invoking routes
  in personas.py, focus_group_ai.py, ai_personas.py
- backend/app/routes/usage.py: GET /api/usage/me (MTD summary by feature),
  GET /api/usage/focus-groups/<id> (owner or admin)
- Registered usage_bp in app/__init__.py
- llm_service._record_usage now emits usage_update WS event to focus group room

Frontend:
- useMyUsage + useFocusGroupUsage hooks
- MyUsage.tsx: personal billing dashboard (cost cards + per-feature table)
- /billing route (ProtectedRoute) + Billing nav link
- FocusGroupSession: quota_warning amber banner with Progress bar,
  quota_exceeded + quota_warning WS events wired via websocketServiceNew

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:43:13 +01:00
Vadym Samoilenko
915c81b8f1 Complete phases D–G: quota enforcement, token invalidation, admin writes, backfill
Backend:
- token_version in JWT (bump_token_version, get_token_version on User model);
  jwt_required checks tv claim → 401 on mismatch; login routes embed version
- Quota pre-flight in all 3 LLM public methods (QuotaExceededError bubbles up)
- AI runner catches QuotaExceededError → sets status paused_quota + emits WS event
- Admin routes: POST /users (create), POST /users/<id>/reset-password,
  POST /pricing, GET /focus-groups with aggregated cost; PUT /users/<id>
  now bumps token_version on disable or role change
- backfill_usage.py: idempotent estimated-event generator for historical data,
  tiktoken for GPT models, char/3.8 for Gemini, --dry-run flag

Frontend:
- 402 interceptor dispatches quota_exceeded CustomEvent
- adminApi: createUser, resetPassword, createPricing, listFocusGroups
- UsersTab: New User dialog + Reset Password in edit dialog
- PricingTab: New Price dialog (model, provider, input/output/cached prices)
- FocusGroupsTab: focus groups table sorted by total cost
- Admin.tsx: 4th tab (Focus Groups)
- FocusGroupSession: admin-only cost badge + dismissable quota exceeded banner

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:34:48 +01:00
Vadym Samoilenko
015e6cc5cc Add Phase D admin panel: user management + usage analytics
Backend: /api/admin/* blueprint with user CRUD (list, get, update,
disable/enable), usage summary aggregation (group by user/model/feature/
day/focus_group), usage event drill-down, and pricing list. Fixed
admin_required decorator (async-safe). Added find_all/count/update
helpers to User model.

Frontend: /admin page (AdminRoute guard, 3 tabs) — Users table with
search/filter/edit dialog, Usage tab with KPI cards + bar chart +
events table, Pricing tab showing active model rows with tier details.
Admin nav link visible only to admin role.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 18:26:05 +01:00
Vadym Samoilenko
2e85fc1acc Fix root cause: naive vs aware datetime crash + stuck AI mode indicator
The autonomous loop was crashing on every decision with:
  TypeError: can't subtract offset-naive and offset-aware datetimes
because MongoDB stores created_at without timezone info but the code
compared it against datetime.now(timezone.utc).

- conversation_context_service: make created_at timezone-aware before
  subtraction (replace tzinfo=utc when naive)
- DiscussionPanel: fix sync effect — when server reports AI mode is
  inactive, always clear localAiModeActive regardless of its value,
  so the "AI is generating..." spinner doesn't get stuck when the
  backend fails/stops before the frontend has confirmed AI mode started

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 19:30:04 +00:00
Vadym Samoilenko
283b31e786 Fix AI mode: race condition, split-brain UI, and stuck local state
- Backend: set status to ai_mode in the route handler before submitting
  to AI runner, eliminating the race condition where frontend's immediate
  status poll read the old status
- Frontend: replace all raw isAiModeActive prop usages with
  effectiveAiModeActive in DiscussionPanel (13 locations) so ReasoningPanel,
  status text, loading indicator, and manual/AI controls all reflect the
  correct state instantly on Start AI Mode click
- Frontend: add useEffect to sync localAiModeActive back to null once
  the parent prop catches up, preventing permanent override after natural
  session end
- These fixes also unblock the 3-second AI message polling which was
  never activating due to isAiModeActive staying false

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 19:02:15 +00:00
Vadym Samoilenko
4b47b334d7 Fix data isolation + conversation/decision 500 errors
Data isolation:
- GET /tasks/<id>: verify requesting user owns the task (403 if not)
- DELETE /tasks/<id>: same ownership check
- GET /tasks/status: add @jwt_required()
- GET /personas/<id>: add ownership check (403 if created_by != user)
- GET /focus-groups/<id>: add ownership check
- GET /focus-groups/<id>/messages: add ownership check
- POST/DELETE /focus-groups/<id>/participants: add ownership check

Fix conversation/decision 500:
- Convert POST /conversation/decision to async 202+background (was synchronous LLM → timed out / LLM errors → 500)
- Frontend polls waitForTaskResult for decision result before calling generateResponseAsync
- GET /conversation/insights: return empty insights (200) on LLM error instead of 500

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 17:02:10 +00:00
Vadym Samoilenko
1b387daacf Migrate task result delivery from WebSocket to HTTP polling
Backend:
- task_manager.py: add result/error/completed_at storage, TTL sweeper (5min), store_task_result() helper
- tasks.py: add GET /<task_id> endpoint returning stored result; cancel route stores 'cancelled' status
- __init__.py: start TTL sweeper on app startup
- All 8 bg functions: store result before emitting lightweight WS hint (no payload data)

Frontend:
- src/lib/taskPolling.ts: waitForTaskResult() — polls GET /tasks/{id} every 2s, WS hint triggers immediate poll, 5min timeout
- src/hooks/useTaskPolling.ts: drop-in replacement for useCancellableGeneration using polling
- Migrate 6 Promise-based WS listeners → waitForTaskResult() in DiscussionPanel, FocusGroupSession (×2), PersonaProfile, PersonaModificationModal, useDiscussionGuideGeneration
- Migrate 3 hook-based consumers → useTaskPolling in AIRecruiter, SyntheticUsers, BulkExportProgressModal

Fixes WS Promise leak: polling survives disconnects, background tabs, page reloads.
WS events retained as zero-payload hints for near-zero latency when connected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 16:46:58 +00:00
Vadym Samoilenko
c7034634e3 Fix all async LLM routes: bypass GCP 30s load balancer timeout
Convert 6 synchronous LLM routes to async 202+WebSocket pattern:
- generate-response (focus_group_ai): persona chat response
- generate-key-themes (focus_group_ai): discussion analysis
- modify-with-ai (personas): AI persona modification
- export-profile (personas): markdown profile export
- describe-asset (focus_groups): image AI description

Each route now returns 202 + task_id immediately, runs LLM in
asyncio background task, delivers result via WebSocket task_completed
event. Frontend listeners updated to wait for ws:task_completed
instead of HTTP response body.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 15:56:54 +00:00
Vadym Samoilenko
770bdee829 Remove console.log debug calls from frontend browser console
Removes all debug/verbose console.log calls across frontend to prevent
sensitive data exposure (session IDs, tokens) and reduce console noise.
Keeps only console.error and console.warn for genuine errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 14:30:56 +00:00
Vadym Samoilenko
f60d86e8cb Fix task_completed WebSocket payload too large
Don't send full persona objects in WS event — only send counts.
Frontend navigates to list page where personas load from API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 14:11:14 +00:00
Vadym Samoilenko
aa4090888d Fix persona generation 504: async flow + remove debug logging
- Backend: /generate-personas-full now returns task_id immediately (202)
  and runs generation as a background asyncio task, delivering results
  via WebSocket task_completed event (bypasses GCP LB 30s timeout)
- Frontend: AIRecruiter listens for ws:task_completed to process personas
  instead of awaiting the long HTTP response
- Remove 53 debug console.log calls from websocketServiceNew.ts including
  session_id exposure and a self-test emit that was firing fake events
- Remove debug logs from WebSocketContextNew, AIRecruiter, personaGenerator

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 14:00:07 +00:00
Vadym Samoilenko
3e1865edbd Apply Jintech security audit remediation (sprint 3) — 87/92 findings fixed
- Fix missing await on FocusGroup.get_messages() (N-L1)
- Replace time.sleep with asyncio.sleep in key_theme_service and focus_group_service (N-P10)
- Replace flask import with quart in focus_groups.py (N-S3)
- Add logger.error before all 500 returns in focus_groups.py (N-P6)
- Add logging to silent except blocks across routes (N-M10, N-M11)
- Add @rate_limit to 6 remaining AI endpoints (N-H4)
- Add --confirm flag to populate scripts before delete_many (S-H2)
- Remove hardcoded Azure ID fallbacks from msal_service.py and msalConfig.ts (A-M2, F-H4)
- Centralize make_serializable() in utils.py, remove duplicates from 3 route files (N-P7)
- Replace all datetime.utcnow() with datetime.now(timezone.utc) across entire backend (M-L2)
- AuthContext.tsx: only mark token validated on 200 success, not on non-401 errors (F-H2)
- Rename authType → auth_type in auth.py (N-S4)
- Add security_report.md and security_report.pdf with full 92-finding status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:51:18 +00:00
michael
b0445de18b Update GPT-5 to GPT-5.2 and lower default reasoning effort to low
Swap model ID from gpt-5 to gpt-5.2 across all backend services,
frontend components, and documentation. Change default reasoning
effort from medium to low for faster responses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 11:24:01 -06:00
Michael Clervi
893b537b67 changed permissions 2025-12-19 19:26:16 +00:00
michael
8a9c7c3cb6 Improve discussion guide discoverability in Focus Group session
Add prominent "Edit Discussion Guide" button to header and enhance accordion
styling with amber color scheme, "Click to Edit" badge, and visual feedback
to help users discover the inline editing capability.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:57:32 -06:00
michael
240a1c09f5 Add field-level inline editing for discussion guide items
Users can now edit individual questions and prompts directly without
entering section edit mode. Edit buttons appear on hover for each item,
allowing inline editing of content, time limits, and probe questions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:43:12 -06:00
michael
28c7bbc055 Improve Focus Group workflow UX
- Rename "Copy Discussion Guide" button to "Import Discussion Guide from Other Project"
- Reorder tabs from Setup → Review & Edit → Participants to Setup → Participant Selection → Review & Edit
- Rename "Participants" tab to "Participant Selection" for clarity

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:30:57 -06:00
michael
08dbd5f5a2 Add word count strength indicator for form text fields
Implements a traffic light indicator beneath paragraph fields to guide users
on content adequacy. Shows red (<33%), yellow (33-99%), or green (≥100%) based
on word count progress toward minimum targets.

Fields updated:
- Research Brief: 150 words minimum
- Discussion Topics: 15 words minimum
- Audience Brief: 150 words minimum
- Research Objective: 150 words minimum

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:25:31 -06:00
michael
22b3ec19a5 Refactor FocusGroupModerator into smaller components and hooks
Extract business logic and UI into reusable pieces:

Custom Hooks:
- useFocusGroupAutoSave: debounced auto-save with retry logic
- useFolderManagement: folder CRUD operations
- usePersonaFiltering: filter state and persona filtering
- useDiscussionGuideGeneration: guide generation and progress

UI Components:
- SaveStatusIndicator: auto-save status display
- FolderSidebar: folder list and management
- PersonaFilterDialog: persona filter modal
- CopyGuideDialog: copy guide from other focus groups

Tab Components:
- SetupTab: form and asset uploader
- ReviewTab: discussion guide viewer
- ParticipantsTab: persona selection grid

Reduces FocusGroupModerator from 2,396 to ~600 lines (75% reduction).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 09:11:21 -06:00
michael
4bf325483e Fix tab not switching to Review after discussion guide generation
The useEffect that reverts to Setup tab on cancellation was incorrectly
firing after successful generation. Added check for discussionGuide to
only revert when generation was actually cancelled (no guide exists).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 08:51:37 -06:00
michael
f0ac4a14de Collapse Customer Data Upload into accordion to reduce visual prominence
The upload section had more prominence than Audience Brief and Research
Objective, causing users to focus there first. Now collapsed by default
with "(Optional)" label to de-emphasize relative to primary inputs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 09:34:28 -06:00
michael
e10a569bc2 Improve Customer Data Upload guidance with clearer instructions
- Add tooltip explaining what data improves persona accuracy
- Add "What data should I upload?" section with bullet list
- Update "What's included in the personas?" section with revised copy
- Add PowerPoint support to allowed file types
- Make FieldTooltip example prop optional for simpler tooltips

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 08:26:45 -06:00
michael
e5aa05739e Fix persona selection grid to use 2x2 layout for consistency
Changed the Select Participants grid from 3 columns to 2 columns on large screens to match the visual pattern used elsewhere in the platform.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 08:05:26 -06:00
michael
5eaf81ec18 Rename Asset Upload to Upload Your Stimulus with clearer description
Updated the asset upload section in the Focus Group flow to use more
intuitive terminology and provide better guidance about its purpose.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 07:55:54 -06:00
michael
ec23bed0b6 Rename Cooper Profile to Attitudinal Profile for clarity
The term "Cooper Profile" confused non-UX users. Renamed to "Attitudinal
Profile" to align with user language and documentation terminology.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 07:44:21 -06:00
michael
bbd3f2c828 Add contextual descriptions to AI model selection dropdowns
Users can now see helpful context next to each model option:
- Gemini 3 Pro (Slow, best for most tasks)
- GPT-4.1 (Fast, best for speed)
- GPT-5 (Slow, best for complex tasks)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:27:57 -06:00
michael
578dc60b11 Add tooltip help icons to Audience Brief and Research Objective fields
Clarifies the distinction between these form fields by adding (?) tooltip
icons with explanatory text and expandable examples. Updates placeholder
text and removes redundant FormDescription elements.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 16:10:21 -06:00
michael
25fcbc826f Redesign Enhance Brief to auto-apply changes with assumptions modal
- Replace suggestions panel with auto-apply: enhanced text now populates form fields automatically
- Add modal showing assumptions made during enhancement (3-5 bullet points)
- Reduce prompt scope by 50% for more focused, impactful enhancements
- Update backend to return enhanced text + assumptions instead of suggestions array

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:57:11 -06:00
michael
567db4227c Move creativity slider to collapsible Advanced Controls section
- Wrap temperature slider in accordion labeled "Advanced Controls"
- Rename "AI Creativity Level (Temperature)" to "Creativity Level"
- Replace numeric labels with descriptive terms (More Focused & Consistent / More Diverse & Creative)
- Remove "Current" value display
- Change default from 1.0 to 0.75 (centered position)
- Update description with user-friendly language about variance and hallucination risk

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 15:07:45 -06:00
michael
03bb650ec6 Replace inline progress bars with modal progress dialogs for better visibility
- Created new reusable ProgressModal component with animated progress bar
- Converted all inline GenerationProgressBar usages to modal dialogs:
  - AIRecruiter.tsx: Persona generation
  - FocusGroupModerator.tsx: Discussion guide generation
  - FocusGroupSession.tsx: Key themes extraction
  - SyntheticUsers.tsx: Persona summary generation
  - PersonaModificationModal.tsx: Persona modification
- Modal features: auto-dismiss after completion, non-dismissible during operation,
  cancel support, progress animation from 0-90% over 54 seconds
- Fixed broken theme generation state calls in FocusGroupSession.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 11:03:31 -06:00
michael
4d9b0afde7 upgraded to Gemini 3.0 Pro (gemini-3-pro-preview) from Gemini 2.5 Pro
- Upgraded google-genai package from 1.31.0 to 1.52.0
- Updated DEFAULT_MODEL in llm_service.py to gemini-3-pro-preview
- Updated all backend routes, services, and models with new model string
- Updated all frontend components with new model string and display labels
- Updated CLAUDE.md documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 10:37:36 -06:00
michael
d3aca7c309 added retry logic and smart field completion for persona generation, increased OpenAI timeout
- persona generation now retries up to 2 times on failure
- missing persona fields are intelligently completed based on context
- expanded required field validation from 5 to 9 fields
- prompt now explicitly lists all required fields with validation instructions
- fixed discussion guide cancellation check to handle object responses

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 09:39:58 -06:00
michael
7c68f5d0e4 truncated long folder names so the buttons are still visible, migrated legacy folders to new format 2025-09-11 10:40:01 -05:00
michael
db3a418d3c aligned manual persona editor tabs and fields with view-only mode - removed tabs and fields from manual edit mode that don't show up in view-only detail mode 2025-09-10 23:05:02 -05:00
michael
f1cc094318 added full persona profile export in bulk actions menu (CSV, JSON and Markdown formats) 2025-09-10 22:38:35 -05:00
michael
abc9731e4a added hierarchical folders (just two levels) with drag and drop management 2025-09-10 19:53:06 -05:00
michael
e29d2a0bb9 made long actions cancellable (like persona generation, etc.), increased variety of persona generation with prompt changes and temperature variable, reduced length of key theme quotes, bug fixes 2025-09-10 16:24:05 -05:00
michael
8288cb9f5e fixed age to be a single number instead of range, fixed language for AI model to include thematic analysis, and added review/revert/save function to AI persona modification mechanism 2025-09-08 16:10:03 -05:00
michael
3c1b082046 fixed messages about parallel generation of personas when only one persona is being generated - actually just removed the parallel language from all the notifications 2025-08-26 14:23:27 -05:00
michael
ece469d15e added generation prompt tracking and viewing in persona detail view (new tab) for audience brief and research objective 2025-08-26 14:15:50 -05:00
michael
065c368aeb added persona modification service - users can adjust individual personas with NLP via LLM 2025-08-26 12:45:15 -05:00
michael
fbef4f42f6 added drag and drop for reordering questions/activities in discussion guide edit mode 2025-08-26 09:52:24 -05:00
michael
3e73259ff3 refactored file uploader and bug fixes 2025-08-13 11:32:56 -05:00
michael
8a5c50cacb refactored image/asset attachment to discussion guide and discussion messages to avoid back end filenames being displayed to user - use metadata system instead. Also added a detailed persona export to markdown. Also, bug fixes 2025-08-12 15:43:34 -05:00
michael
9a3baa9d97 converted message polling to websockets 2025-08-10 18:08:34 -05:00
michael
da8639aee8 fixed folders again, bug fixes for gpt-5, adjusted response length calculation, cosmetic UI changes, other bug fixes 2025-08-09 10:08:45 -05:00
michael
fbb444037a fixed folders to be database instead of local storage based, implemented gpt-5, fixed key theme export quotes 2025-08-09 06:38:49 -05:00
michael
3c9518e3ec various bug fixes and UI tweaks 2025-08-07 16:34:37 -05:00
michael
8dcbe7efee various bug fixes and feature additions per Alec's requests 2025-08-06 19:29:27 -05:00
michael
b649793013 added gpt-4.1 support among other things 2025-08-05 17:38:13 -05:00