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>
This commit is contained in:
michael 2025-12-01 10:37:36 -06:00
parent d3aca7c309
commit 4d9b0afde7
19 changed files with 85 additions and 63 deletions

View file

@ -15,7 +15,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **Start Backend**: `python run.py` (from backend/ directory)
- **Backend Server**: Runs on port 5137 with Hypercorn ASGI server
- **Database**: MongoDB with PyMongo
- **Authentication**: JWT tokens via Flask-JWT-Extended
- **Authentication**: Custom Quart-compatible JWT (not Flask-JWT-Extended)
## Testing
**Python Backend**: After modifying any Python files:
@ -26,9 +26,30 @@ python -c "from app import create_app; app = create_app()" # Test app creation
```
**Frontend**: Run `npm run build` to verify TypeScript compilation
## Architecture Overview
### Real-Time Communication
The application uses Socket.IO for real-time WebSocket communication between frontend and backend:
- **Backend**: `python-socketio` with `AsyncServer` wrapped in ASGI app
- **Frontend**: `socket.io-client` managed via `WebSocketContext`
- WebSocket manager (`websocket_manager_async.py`) handles room-based messaging for focus group sessions
### Autonomous Conversation System
Focus group sessions can run autonomously with AI-driven conversations:
- `ai_runner_service.py` - Manages background task execution for autonomous mode
- `autonomous_conversation_controller.py` - Orchestrates multi-persona conversations
- `conversation_decision_service.py` - Determines next speaker and conversation flow
- `conversation_context_service.py` - Maintains conversation state and history
### LLM Integration
Multi-model support through `llm_service.py`:
- **Google Gemini** (`gemini-3-pro-preview`) - Default model
- **OpenAI** (`gpt-4.1`, `gpt-5`) - Alternative models
- Prompts are stored as markdown templates in `/backend/prompts/`
## Code Style Guidelines
- **Imports**: Group imports by source (React, third-party, local)
- **Types**: Use TypeScript. Project allows nullable types (`strictNullChecks: false`)
- **Types**: Use TypeScript. Project allows nullable types (`strictNullChecks: false`)
- **Components**: Use functional components with hooks
- **Naming**: Use PascalCase for components, camelCase for variables/functions
- **Formatting**: Follow ESLint recommendations, focus on readability
@ -41,12 +62,13 @@ python -c "from app import create_app; app = create_app()" # Test app creation
## Project Stack
**Frontend**: Vite, React 18, TypeScript, Tailwind CSS, shadcn-ui
**Backend**: Flask 2.2.3, Hypercorn, PyMongo, JWT Extended
**Key Libraries**:
**Backend**: Quart (async Flask), Hypercorn ASGI, PyMongo, python-socketio
**Key Libraries**:
- UI: Radix UI components, Lucide React icons
- State: TanStack Query, React Hook Form with Zod validation
- Routing: React Router DOM
- AI/LLM: OpenAI, Google Generative AI
- AI/LLM: OpenAI, Google Generative AI (genai)
- Real-time: Socket.IO (client and server)
- Charts: Recharts
- Drag & Drop: DND Kit
@ -58,15 +80,15 @@ python -c "from app import create_app; app = create_app()" # Test app creation
## File Organization
- **Backend Services**: `/backend/app/services/` - Business logic and AI integrations
- **Backend Models**: `/backend/app/models/` - Data models (User, FocusGroup, Persona)
- **Backend Routes**: `/backend/app/routes/` - API endpoints
- **AI Prompts**: `/backend/prompts/` - LLM prompt templates
- **Frontend Components**:
- **Backend Models**: `/backend/app/models/` - Data models (User, FocusGroup, Persona, Folder)
- **Backend Routes**: `/backend/app/routes/` - API endpoints (auth, personas, focus-groups, ai-personas, folders, tasks)
- **AI Prompts**: `/backend/prompts/` - LLM prompt templates (markdown files loaded by `prompt_loader.py`)
- **Frontend Components**:
- `/src/components/ui/` - Reusable shadcn-ui components
- `/src/components/focus-group-session/` - Focus group specific components
- `/src/components/focus-group-session/` - Focus group session UI (DiscussionPanel, ParticipantPanel, ThemesPanel, etc.)
- `/src/components/persona/` - Persona management components
- **Types**: `/src/types/` - TypeScript type definitions
- **Contexts**: `/src/contexts/` - React context providers
- **Contexts**: `/src/contexts/` - React context providers (AuthContext, WebSocketContext, NavigationContext)
## Environment Configuration

View file

@ -82,7 +82,7 @@ class FocusGroup:
# Set default LLM model if not provided
if "llm_model" not in focus_group_data:
focus_group_data["llm_model"] = "gemini-2.5-pro"
focus_group_data["llm_model"] = "gemini-3-pro-preview"
# Set default GPT-5 parameters if not provided
if "reasoning_effort" not in focus_group_data:

View file

@ -67,7 +67,7 @@ async def generate_basic_profiles():
temperature = 1.0
customer_data_session_id = data.get('customer_data_session_id') # Optional parameter
llm_model = data.get('llm_model', 'gemini-2.5-pro') # Optional parameter with default
llm_model = data.get('llm_model', 'gemini-3-pro-preview') # Optional parameter with default
try:
# Register current task for cancellation
@ -197,7 +197,7 @@ async def complete_and_save_persona():
temperature = 1.0
customer_data_session_id = data.get('customer_data_session_id') # Optional parameter
llm_model = data.get('llm_model', 'gemini-2.5-pro') # Optional parameter with default
llm_model = data.get('llm_model', 'gemini-3-pro-preview') # Optional parameter with default
# Get persona name for logging
persona_name = basic_profile.get('name', 'Unknown')
@ -816,7 +816,7 @@ async def batch_generate_summaries():
if not (0 <= temperature <= 1.5):
temperature = 1.0
llm_model = data.get('llm_model', 'gemini-2.5-pro') # Optional parameter with default
llm_model = data.get('llm_model', 'gemini-3-pro-preview') # Optional parameter with default
# Log the request with model information
print(f"🔄 Backend: Received batch-generate-summaries request for {len(persona_ids)} personas with model: {llm_model}")
@ -1013,7 +1013,7 @@ async def generate_personas_full():
"count": 5,
"temperature": 0.8,
"customer_data_session_id": "optional_session_id",
"llm_model": "gemini-2.5-pro",
"llm_model": "gemini-3-pro-preview",
"target_folder_id": "optional_folder_id"
}
@ -1038,7 +1038,7 @@ async def generate_personas_full():
temperature = 1.0
customer_data_session_id = data.get('customer_data_session_id')
llm_model = data.get('llm_model', 'gemini-2.5-pro')
llm_model = data.get('llm_model', 'gemini-3-pro-preview')
target_folder_id = data.get('target_folder_id')
try:

View file

@ -84,7 +84,7 @@ async def generate_ai_response():
import datetime
log_msg = f"🤖 [{datetime.datetime.now()}] AI RESPONSE - Focus group keys: {list(focus_group.keys())}\n"
log_msg += f"🤖 [{datetime.datetime.now()}] AI RESPONSE - Raw llm_model from DB: '{focus_group.get('llm_model')}' (type: {type(focus_group.get('llm_model'))})\n"
log_msg += f"🤖 [{datetime.datetime.now()}] AI RESPONSE - Using model: {llm_model or 'default (gemini-2.5-pro)'} for focus group {focus_group_id}\n"
log_msg += f"🤖 [{datetime.datetime.now()}] AI RESPONSE - Using model: {llm_model or 'default (gemini-3-pro-preview)'} for focus group {focus_group_id}\n"
with open('/tmp/focus_group_debug.log', 'a') as f:
f.write(log_msg)
f.flush()
@ -93,7 +93,7 @@ async def generate_ai_response():
current_app.logger.info(f"🔍 DEBUG: Focus group data keys: {list(focus_group.keys())}")
current_app.logger.info(f"🔍 DEBUG: Raw llm_model value from DB: '{focus_group.get('llm_model')}' (type: {type(focus_group.get('llm_model'))})")
current_app.logger.info(f"🤖 Generating AI response using model: {llm_model or 'default (gemini-2.5-pro)'} for focus group {focus_group_id}")
current_app.logger.info(f"🤖 Generating AI response using model: {llm_model or 'default (gemini-3-pro-preview)'} for focus group {focus_group_id}")
# Validate persona exists
persona = await Persona.find_by_id(persona_id)

View file

@ -173,7 +173,7 @@ async def modify_persona_with_ai(persona_id):
Request body should include:
- modification_prompt: Natural language description of desired changes
- llm_model: Model to use (defaults to 'gemini-2.5-pro')
- llm_model: Model to use (defaults to 'gemini-3-pro-preview')
- reasoning_effort: For GPT-5 (minimal, low, medium, high)
- verbosity: For GPT-5 (low, medium, high)
- preview_only: If true, returns modified data without saving to database (defaults to false)
@ -188,7 +188,7 @@ async def modify_persona_with_ai(persona_id):
if not modification_prompt:
return jsonify({"error": "modification_prompt is required"}), 400
llm_model = request_data.get('llm_model', 'gemini-2.5-pro')
llm_model = request_data.get('llm_model', 'gemini-3-pro-preview')
reasoning_effort = request_data.get('reasoning_effort', 'medium')
verbosity = request_data.get('verbosity', 'medium')
preview_only = request_data.get('preview_only', False)

View file

@ -218,7 +218,7 @@ async def _generate_basic_personas_attempt(
# Log the LLM API call with attempt number
attempt_text = f" (attempt {attempt})" if attempt > 1 else ""
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-2.5-pro'} for basic persona generation{attempt_text}")
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-3-pro-preview'} for basic persona generation{attempt_text}")
raw_response = await LLMService.generate_content(
prompt=final_prompt,
@ -504,7 +504,7 @@ async def generate_persona(
# Log the LLM API call
persona_name = basic_persona.get('name', 'Unknown') if basic_persona else 'New Persona'
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-2.5-pro'} for detailed persona generation of '{persona_name}'")
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-3-pro-preview'} for detailed persona generation of '{persona_name}'")
persona_data = await LLMService.generate_structured_response(
prompt=final_prompt,
@ -589,7 +589,7 @@ async def generate_persona_summary(
# Log the LLM API call
persona_name = persona_data.get('name', 'Unknown')
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-2.5-pro'} for summary generation of '{persona_name}'")
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-3-pro-preview'} for summary generation of '{persona_name}'")
raw_response = await LLMService.generate_content(
prompt=final_prompt,
@ -694,7 +694,7 @@ async def generate_persona_download_summary(
# Log the LLM API call
persona_name = persona_data.get('name', 'Unknown')
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-2.5-pro'} for download summary of '{persona_name}'")
print(f"🤖 Backend: Making LLM API call to {llm_model or 'gemini-3-pro-preview'} for download summary of '{persona_name}'")
# Generate the markdown content directly
markdown_response = await LLMService.generate_content(

View file

@ -664,7 +664,7 @@ class AutonomousConversationController:
llm_model = focus_group.get('llm_model')
reasoning_effort = focus_group.get('reasoning_effort', 'medium')
verbosity = focus_group.get('verbosity', 'medium')
self.logger.info(f"🤖 Autonomous conversation using model: {llm_model or 'default (gemini-2.5-pro)'} for focus group {self.focus_group_id}")
self.logger.info(f"🤖 Autonomous conversation using model: {llm_model or 'default (gemini-3-pro-preview)'} for focus group {self.focus_group_id}")
# Get recent messages
messages = await FocusGroup.get_messages(self.focus_group_id)

View file

@ -52,7 +52,7 @@ async def generate_persona_response(
if llm_model == 'gpt-5':
print(f" - llm_model: {llm_model} (reasoning_effort: {reasoning_effort or 'medium'}, verbosity: {verbosity or 'medium'}) [using Responses API]")
else:
print(f" - llm_model: {llm_model or 'default (gemini-2.5-pro)'}")
print(f" - llm_model: {llm_model or 'default (gemini-3-pro-preview)'}")
# Import LLMService at the top to avoid scoping issues
from app.services.llm_service import LLMService

View file

@ -224,7 +224,7 @@ class FocusGroupService:
'content': question.get('content', 'No content')[:100] + '...'
})
logger.info(f"=== CREATIVE REVIEW VALIDATION RESULTS (Model: {llm_model or 'gemini-2.5-pro'}) ===")
logger.info(f"=== CREATIVE REVIEW VALIDATION RESULTS (Model: {llm_model or 'gemini-3-pro-preview'}) ===")
logger.info(f"Found {creative_review_count} creative_review activities for {len(uploaded_assets)} uploaded assets")
if creative_review_activities:
@ -236,7 +236,7 @@ class FocusGroupService:
# If no creative review activities were generated, retry with enhanced prompt
if creative_review_count == 0:
logger.warning(f"❌ WARNING: No creative_review activities generated despite {len(uploaded_assets)} uploaded assets!")
logger.warning(f"❌ This suggests {llm_model or 'gemini-2.5-pro'} is not following the creative asset instructions")
logger.warning(f"❌ This suggests {llm_model or 'gemini-3-pro-preview'} is not following the creative asset instructions")
# For GPT models, if this was already the enhanced prompt, we have a serious issue
if llm_model and llm_model.startswith('gpt') and attempt < max_retries:

View file

@ -41,7 +41,7 @@ class KeyThemeService:
"""
logger = logging.getLogger(__name__)
logger.info(f"Starting key theme generation for focus group {focus_group_id} with temperature {temperature}")
logger.info(f"Using LLM model: {llm_model or 'default (gemini-2.5-pro)'}")
logger.info(f"Using LLM model: {llm_model or 'default (gemini-3-pro-preview)'}")
try:
# Get the focus group
@ -105,7 +105,7 @@ class KeyThemeService:
"""
logger = logging.getLogger(__name__)
logger.info(f"Beginning theme extraction from {len(messages)} messages")
logger.info(f"Theme extraction using LLM model: {llm_model or 'default (gemini-2.5-pro)'}")
logger.info(f"Theme extraction using LLM model: {llm_model or 'default (gemini-3-pro-preview)'}")
try:
# Load and prepare the prompt for the LLM
@ -135,7 +135,7 @@ class KeyThemeService:
for attempt in range(max_retries):
attempt_num = attempt + 1
logger.info(f"Attempt {attempt_num}/{max_retries}: Calling LLM ({llm_model or 'gemini-2.5-pro'}) for theme generation")
logger.info(f"Attempt {attempt_num}/{max_retries}: Calling LLM ({llm_model or 'gemini-3-pro-preview'}) for theme generation")
try:
themes = await LLMService.generate_structured_array(
@ -145,7 +145,7 @@ class KeyThemeService:
model_name=llm_model
)
logger.info(f"Attempt {attempt_num}/{max_retries}: LLM ({llm_model or 'gemini-2.5-pro'}) call successful, received {len(themes)} themes")
logger.info(f"Attempt {attempt_num}/{max_retries}: LLM ({llm_model or 'gemini-3-pro-preview'}) call successful, received {len(themes)} themes")
# Validate the response structure
validated_themes = []
@ -175,7 +175,7 @@ class KeyThemeService:
validated_themes.append(validated_theme)
logger.info(f"Theme generation completed successfully with {len(validated_themes)} validated themes using {llm_model or 'gemini-2.5-pro'}")
logger.info(f"Theme generation completed successfully with {len(validated_themes)} validated themes using {llm_model or 'gemini-3-pro-preview'}")
return validated_themes
except LLMServiceError as e:

View file

@ -25,12 +25,12 @@ gemini_client = genai.Client(api_key=GEMINI_API_KEY)
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY', 'REDACTED_OPENAI_KEY')
openai_client = AsyncOpenAI(api_key=OPENAI_API_KEY, timeout=600.0)
# The default model we're using
DEFAULT_MODEL = "gemini-2.5-pro"
# The default model we're using
DEFAULT_MODEL = "gemini-3-pro-preview"
# Supported models
SUPPORTED_MODELS = {
'gemini-2.5-pro': 'gemini',
'gemini-3-pro-preview': 'gemini',
'gpt-4.1': 'openai',
'gpt-5': 'openai'
}

View file

@ -134,7 +134,7 @@ class PersonaModificationService:
async def modify_persona(
persona_id: str,
modification_prompt: str,
llm_model: str = 'gemini-2.5-pro',
llm_model: str = 'gemini-3-pro-preview',
reasoning_effort: str = 'medium',
verbosity: str = 'medium',
max_retries: int = 3,

View file

@ -159,7 +159,7 @@ export default function AIRecruiter({ targetFolderId, targetFolderName }: AIRecr
if (response.partial_success || (response.errors && response.errors.length > 0)) {
// Some personas succeeded but others failed
toast.success("Some personas generated successfully", {
description: `${personas.length} synthetic personas were created using ${values.llm_model || 'Gemini 2.5 Pro'}. ${response.errors?.length || 0} failed due to timeout or other errors.`,
description: `${personas.length} synthetic personas were created using ${values.llm_model || 'Gemini 3 Pro'}. ${response.errors?.length || 0} failed due to timeout or other errors.`,
duration: 8000
});
@ -175,7 +175,7 @@ export default function AIRecruiter({ targetFolderId, targetFolderName }: AIRecr
} else {
// All personas succeeded
toast.success("Personas generated and saved successfully", {
description: `${personas.length} synthetic personas have been created using ${values.llm_model || 'Gemini 2.5 Pro'} and saved ${targetFolderId ? `to the "${targetFolderName}" folder` : 'to the database'}.`
description: `${personas.length} synthetic personas have been created using ${values.llm_model || 'Gemini 3 Pro'} and saved ${targetFolderId ? `to the "${targetFolderName}" folder` : 'to the database'}.`
});
}

View file

@ -527,7 +527,7 @@ export default function FocusGroupModerator({ draftToEdit, onDraftSaved, preSele
focusGroupName: "",
discussionTopics: "",
duration: "60",
llm_model: "gemini-2.5-pro",
llm_model: "gemini-3-pro-preview",
reasoning_effort: "medium",
verbosity: "medium",
},
@ -554,7 +554,7 @@ export default function FocusGroupModerator({ draftToEdit, onDraftSaved, preSele
objective: values.researchBrief || '',
topic: values.discussionTopics || '',
duration: values.duration ? parseInt(values.duration) : 60,
llm_model: values.llm_model || 'gemini-2.5-pro',
llm_model: values.llm_model || 'gemini-3-pro-preview',
reasoning_effort: values.reasoning_effort || 'medium',
verbosity: values.verbosity || 'medium',
participants: selectedParticipants,
@ -692,7 +692,7 @@ export default function FocusGroupModerator({ draftToEdit, onDraftSaved, preSele
topic: values.discussionTopics || '',
description: values.researchBrief || '',
objective: values.researchBrief || '',
llm_model: values.llm_model || 'gemini-2.5-pro',
llm_model: values.llm_model || 'gemini-3-pro-preview',
reasoning_effort: values.reasoning_effort || 'medium',
verbosity: values.verbosity || 'medium',
discussionGuide: sourceFocusGroup.discussionGuide
@ -860,7 +860,7 @@ export default function FocusGroupModerator({ draftToEdit, onDraftSaved, preSele
objective: draftToEdit.description || draftToEdit.objective || '',
topic: draftToEdit.topic || '',
duration: draftToEdit.duration || 60,
llm_model: draftToEdit.llm_model || 'gemini-2.5-pro',
llm_model: draftToEdit.llm_model || 'gemini-3-pro-preview',
reasoning_effort: draftToEdit.reasoning_effort || 'medium',
verbosity: draftToEdit.verbosity || 'medium',
participants: draftToEdit.participants || [],
@ -1580,7 +1580,7 @@ true;
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="gemini-2.5-pro">Gemini 2.5 Pro</SelectItem>
<SelectItem value="gemini-3-pro-preview">Gemini 3 Pro</SelectItem>
<SelectItem value="gpt-4.1">GPT-4.1</SelectItem>
<SelectItem value="gpt-5">GPT-5</SelectItem>
</SelectContent>

View file

@ -70,7 +70,7 @@ export default function AIRecruiterForm({ onSubmit, isGenerating }: AIRecruiterF
researchObjective: "",
personaCount: "5",
temperature: 1.0,
llm_model: "gemini-2.5-pro",
llm_model: "gemini-3-pro-preview",
},
});
@ -380,7 +380,7 @@ export default function AIRecruiterForm({ onSubmit, isGenerating }: AIRecruiterF
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="gemini-2.5-pro">Gemini 2.5 Pro</SelectItem>
<SelectItem value="gemini-3-pro-preview">Gemini 3 Pro</SelectItem>
<SelectItem value="gpt-4.1">GPT-4.1</SelectItem>
<SelectItem value="gpt-5">GPT-5</SelectItem>
</SelectContent>

View file

@ -70,7 +70,7 @@ export default function PersonaModificationModal({
resolver: zodResolver(modificationFormSchema),
defaultValues: {
modificationPrompt: "",
llm_model: "gemini-2.5-pro",
llm_model: "gemini-3-pro-preview",
reasoning_effort: "medium",
verbosity: "medium",
},
@ -221,7 +221,7 @@ export default function PersonaModificationModal({
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="gemini-2.5-pro">Gemini 2.5 Pro</SelectItem>
<SelectItem value="gemini-3-pro-preview">Gemini 3 Pro</SelectItem>
<SelectItem value="gpt-4.1">GPT-4.1</SelectItem>
<SelectItem value="gpt-5">GPT-5</SelectItem>
</SelectContent>

View file

@ -730,11 +730,11 @@ const FocusGroupSession = () => {
duration: data.duration || 60,
topic: data.topic || 'general',
discussionGuide: data.discussionGuide || '',
llm_model: data.llm_model || 'gemini-2.5-pro'
llm_model: data.llm_model || 'gemini-3-pro-preview'
};
setFocusGroup(focusGroupData);
setSelectedModel(focusGroupData.llm_model || 'gemini-2.5-pro');
setSelectedModel(focusGroupData.llm_model || 'gemini-3-pro-preview');
setSelectedReasoningEffort(focusGroupData.reasoning_effort || 'medium');
setSelectedVerbosity(focusGroupData.verbosity || 'medium');
@ -816,7 +816,7 @@ const FocusGroupSession = () => {
} : null);
toastService.success('AI Model Updated', {
description: `Focus group will now use ${
newModel === 'gemini-2.5-pro' ? 'Gemini 2.5 Pro' :
newModel === 'gemini-3-pro-preview' ? 'Gemini 3 Pro' :
newModel === 'gpt-4.1' ? 'GPT-4.1' :
newModel === 'gpt-5' ? 'GPT-5' : newModel
} for AI responses`
@ -869,11 +869,11 @@ const FocusGroupSession = () => {
duration: data.duration || 60,
topic: data.topic || 'general',
discussionGuide: data.discussionGuide || '',
llm_model: data.llm_model || 'gemini-2.5-pro'
llm_model: data.llm_model || 'gemini-3-pro-preview'
};
setFocusGroup(focusGroupData);
setSelectedModel(focusGroupData.llm_model || 'gemini-2.5-pro');
setSelectedModel(focusGroupData.llm_model || 'gemini-3-pro-preview');
setSelectedReasoningEffort(focusGroupData.reasoning_effort || 'medium');
setSelectedVerbosity(focusGroupData.verbosity || 'medium');
@ -1932,7 +1932,7 @@ const FocusGroupSession = () => {
<Bot className="h-3 w-3 text-slate-500 mr-1" />
<Badge variant="secondary" className="text-xs">
{focusGroup.llm_model === 'gpt-4.1' ? 'GPT-4.1' :
focusGroup.llm_model === 'gpt-5' ? 'GPT-5' : 'Gemini 2.5 Pro'}
focusGroup.llm_model === 'gpt-5' ? 'GPT-5' : 'Gemini 3 Pro'}
</Badge>
</div>
</div>
@ -2294,7 +2294,7 @@ const FocusGroupSession = () => {
<span className="text-sm font-medium">Current Model:</span>
<Badge variant="secondary">
{focusGroup?.llm_model === 'gpt-4.1' ? 'GPT-4.1' :
focusGroup?.llm_model === 'gpt-5' ? 'GPT-5' : 'Gemini 2.5 Pro'}
focusGroup?.llm_model === 'gpt-5' ? 'GPT-5' : 'Gemini 3 Pro'}
</Badge>
</div>
@ -2308,7 +2308,7 @@ const FocusGroupSession = () => {
<SelectValue placeholder="Select AI model" />
</SelectTrigger>
<SelectContent>
<SelectItem value="gemini-2.5-pro">Gemini 2.5 Pro</SelectItem>
<SelectItem value="gemini-3-pro-preview">Gemini 3 Pro</SelectItem>
<SelectItem value="gpt-4.1">GPT-4.1</SelectItem>
<SelectItem value="gpt-5">GPT-5</SelectItem>
</SelectContent>
@ -2364,7 +2364,7 @@ Controls how thoroughly GPT-5 thinks and how detailed responses are
)}
<div className="text-xs text-slate-600">
<p><strong>Gemini 2.5 Pro:</strong> Google's advanced model, great for creative and analytical tasks.</p>
<p><strong>Gemini 3 Pro:</strong> Google's advanced model, great for creative and analytical tasks.</p>
<p><strong>GPT-4.1:</strong> OpenAI's latest model, excellent for conversational and reasoning tasks.</p>
<p><strong>GPT-5:</strong> OpenAI's newest model with advanced reasoning and customizable response styles.</p>
</div>

View file

@ -142,7 +142,7 @@ const SyntheticUsers = () => {
});
// LLM selection for download
const [downloadLlmModalOpen, setDownloadLlmModalOpen] = useState(false);
const [selectedDownloadLlmModel, setSelectedDownloadLlmModel] = useState<string>('gemini-2.5-pro');
const [selectedDownloadLlmModel, setSelectedDownloadLlmModel] = useState<string>('gemini-3-pro-preview');
// Bulk export no longer needs state - direct download
@ -1057,7 +1057,7 @@ const SyntheticUsers = () => {
summaryGenerationControls.completeGeneration();
// Show success toast with details including model information
const modelDisplayName = selectedDownloadLlmModel === 'gpt-4.1' ? 'GPT-4.1' : 'Gemini 2.5 Pro';
const modelDisplayName = selectedDownloadLlmModel === 'gpt-4.1' ? 'GPT-4.1' : 'Gemini 3 Pro';
if (summary_stats.total_successful === summary_stats.total_requested) {
toastService.success("Persona summary downloaded", {
description: `Successfully processed all ${summary_stats.total_successful} persona${summary_stats.total_successful !== 1 ? 's' : ''} from "${folderName}" using ${modelDisplayName}`
@ -1929,9 +1929,9 @@ const SyntheticUsers = () => {
className="space-y-3"
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="gemini-2.5-pro" id="download-gemini" />
<RadioGroupItem value="gemini-3-pro-preview" id="download-gemini" />
<Label htmlFor="download-gemini" className="text-sm font-medium">
Gemini 2.5 Pro
Gemini 3 Pro
</Label>
</div>
<div className="flex items-center space-x-2">

View file

@ -27,7 +27,7 @@ export async function generateSyntheticPersonas(
): Promise<{personas: Persona[], task_id?: string, partial_success?: boolean, errors?: any[]}> {
// Debug logging for folder and model
console.log(`generateSyntheticPersonas called with targetFolderId: ${targetFolderId || 'none'}`);
console.log(`🔄 generateSyntheticPersonas using model: ${llmModel || 'gemini-2.5-pro'}`);
console.log(`🔄 generateSyntheticPersonas using model: ${llmModel || 'gemini-3-pro-preview'}`);
try {
// We'll use the two-stage approach