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
This commit is contained in:
parent
1b977ec517
commit
8288cb9f5e
16 changed files with 226 additions and 65 deletions
BIN
.DS_Store
vendored
BIN
.DS_Store
vendored
Binary file not shown.
Binary file not shown.
|
|
@ -165,6 +165,7 @@ async def modify_persona_with_ai(persona_id):
|
|||
- llm_model: Model to use (defaults to 'gemini-2.5-pro')
|
||||
- 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)
|
||||
"""
|
||||
try:
|
||||
# Get request data
|
||||
|
|
@ -179,8 +180,10 @@ async def modify_persona_with_ai(persona_id):
|
|||
llm_model = request_data.get('llm_model', 'gemini-2.5-pro')
|
||||
reasoning_effort = request_data.get('reasoning_effort', 'medium')
|
||||
verbosity = request_data.get('verbosity', 'medium')
|
||||
preview_only = request_data.get('preview_only', False)
|
||||
|
||||
print(f"🤖 Backend: Modifying persona {persona_id} with {llm_model}")
|
||||
mode_text = "previewing" if preview_only else "modifying"
|
||||
print(f"🤖 Backend: {mode_text.title()} persona {persona_id} with {llm_model}")
|
||||
print(f"📝 Modification prompt: {modification_prompt[:100]}...")
|
||||
|
||||
# Call the modification service
|
||||
|
|
@ -189,13 +192,16 @@ async def modify_persona_with_ai(persona_id):
|
|||
modification_prompt=modification_prompt,
|
||||
llm_model=llm_model,
|
||||
reasoning_effort=reasoning_effort,
|
||||
verbosity=verbosity
|
||||
verbosity=verbosity,
|
||||
preview_only=preview_only
|
||||
)
|
||||
|
||||
success_message = "Persona preview generated successfully" if preview_only else "Persona modified successfully"
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "Persona modified successfully",
|
||||
"persona": make_serializable(modified_persona_data)
|
||||
"message": success_message,
|
||||
"persona": make_serializable(modified_persona_data),
|
||||
"preview_only": preview_only
|
||||
}), 200
|
||||
|
||||
except PersonaModificationError as e:
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -178,6 +178,20 @@ async def generate_basic_personas(
|
|||
raise PersonaGenerationError(
|
||||
f"Persona {i+1} is missing required fields: {', '.join(missing_fields)}"
|
||||
)
|
||||
|
||||
# Validate that age is a single number, not a range
|
||||
age_value = persona.get("age", "")
|
||||
if isinstance(age_value, str) and "-" in age_value:
|
||||
raise PersonaGenerationError(
|
||||
f"Persona {i+1} has an invalid age range '{age_value}'. Age must be a single specific number (e.g., '35', not '35-42')"
|
||||
)
|
||||
|
||||
# Validate that age is numeric
|
||||
age_str = str(age_value).strip()
|
||||
if not age_str.isdigit():
|
||||
raise PersonaGenerationError(
|
||||
f"Persona {i+1} has an invalid age '{age_value}'. Age must be a numeric value (e.g., '35')"
|
||||
)
|
||||
|
||||
return personas_array
|
||||
|
||||
|
|
@ -271,6 +285,20 @@ async def generate_persona(
|
|||
if missing_fields:
|
||||
raise PersonaGenerationError(f"Generated persona is missing required fields: {', '.join(missing_fields)}")
|
||||
|
||||
# Validate that age is a single number, not a range
|
||||
age_value = persona_data.get("age", "")
|
||||
if isinstance(age_value, str) and "-" in age_value:
|
||||
raise PersonaGenerationError(
|
||||
f"Generated persona has an invalid age range '{age_value}'. Age must be a single specific number (e.g., '35', not '35-42')"
|
||||
)
|
||||
|
||||
# Validate that age is numeric
|
||||
age_str = str(age_value).strip()
|
||||
if not age_str.isdigit():
|
||||
raise PersonaGenerationError(
|
||||
f"Generated persona has an invalid age '{age_value}'. Age must be a numeric value (e.g., '35')"
|
||||
)
|
||||
|
||||
# Generate ID if missing
|
||||
if "id" not in persona_data:
|
||||
persona_data["id"] = f"generated-{uuid.uuid4()}"
|
||||
|
|
|
|||
|
|
@ -137,7 +137,8 @@ class PersonaModificationService:
|
|||
llm_model: str = 'gemini-2.5-pro',
|
||||
reasoning_effort: str = 'medium',
|
||||
verbosity: str = 'medium',
|
||||
max_retries: int = 3
|
||||
max_retries: int = 3,
|
||||
preview_only: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Modify a persona using AI based on natural language instructions.
|
||||
|
|
@ -149,6 +150,7 @@ class PersonaModificationService:
|
|||
reasoning_effort: Reasoning effort for GPT-5 (minimal, low, medium, high)
|
||||
verbosity: Response verbosity for GPT-5 (low, medium, high)
|
||||
max_retries: Maximum number of retries for invalid responses
|
||||
preview_only: If True, returns modified data without saving to database
|
||||
|
||||
Returns:
|
||||
Dictionary containing the modified persona data
|
||||
|
|
@ -211,13 +213,16 @@ class PersonaModificationService:
|
|||
sanitized_persona, modified_persona_data
|
||||
)
|
||||
|
||||
# Update the persona in the database
|
||||
success = await Persona.update(persona_id, modified_persona_data)
|
||||
if not success:
|
||||
raise PersonaModificationError("Failed to update persona in database")
|
||||
# Update the persona in the database (only if not preview mode)
|
||||
if not preview_only:
|
||||
success = await Persona.update(persona_id, modified_persona_data)
|
||||
if not success:
|
||||
raise PersonaModificationError("Failed to update persona in database")
|
||||
logger.info(f"Successfully modified persona {persona_id}")
|
||||
else:
|
||||
logger.info(f"Generated preview for persona {persona_id} (not saved to database)")
|
||||
|
||||
# Return the modified persona data
|
||||
logger.info(f"Successfully modified persona {persona_id}")
|
||||
return modified_persona_data
|
||||
|
||||
except LLMServiceError as e:
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ EXAMPLE_JSON_START
|
|||
]
|
||||
EXAMPLE_JSON_END
|
||||
|
||||
CRITICAL AGE REQUIREMENT: The "age" field MUST contain a single, specific number (e.g., "35", "42") representing the persona's exact age. DO NOT use age ranges (e.g., "35-42", "30-35"). These are individual personas and each person has one specific age, not a range.
|
||||
|
||||
IMPORTANT:
|
||||
- Return EXACTLY {count} personas in a JSON array format
|
||||
- Do not include any comments (like "// Second persona") in the JSON
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ EXAMPLE_JSON_START
|
|||
{
|
||||
"id": "generated-[unique-id]",
|
||||
"name": "[Full Name]",
|
||||
"age": "[Age Range]",
|
||||
"age": "[Specific Age]",
|
||||
"gender": "[Gender]",
|
||||
"occupation": "[Job Title]",
|
||||
"education": "[Education Level]",
|
||||
|
|
@ -106,4 +106,6 @@ EXAMPLE_JSON_START
|
|||
}
|
||||
EXAMPLE_JSON_END
|
||||
|
||||
CRITICAL AGE REQUIREMENT: The "age" field MUST contain a single, specific number (e.g., "35", "42") as a string, representing the persona's exact age. DO NOT use age ranges (e.g., "35-42", "30-35"). These are individual personas and each person has one specific age, not a range.
|
||||
|
||||
IMPORTANT: Return ONLY the JSON object with no additional text, explanations, or formatting.
|
||||
4
dist/index.html
vendored
4
dist/index.html
vendored
|
|
@ -7,8 +7,8 @@
|
|||
<meta name="description" content="Lovable Generated Project" />
|
||||
<meta name="author" content="Lovable" />
|
||||
<meta property="og:image" content="/og-image.png" />
|
||||
<script type="module" crossorigin src="/semblance/assets/index-CLQA4rqQ.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/semblance/assets/index-8o0iGAjY.css">
|
||||
<script type="module" crossorigin src="/semblance/assets/index-DeCgXQlM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/semblance/assets/index-D95t4u2x.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -1556,7 +1556,7 @@ true;
|
|||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
Choose which AI model to use for generating responses and discussion guides
|
||||
Choose which AI model to use for generating responses, discussion guides, and thematic analysis
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
|
|
|
|||
|
|
@ -50,14 +50,14 @@ interface PersonaModificationModalProps {
|
|||
persona: Persona;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onPersonaModified: (modifiedPersona: Persona) => void;
|
||||
onPersonaPreview: (modifiedPersona: Persona) => void;
|
||||
}
|
||||
|
||||
export default function PersonaModificationModal({
|
||||
persona,
|
||||
isOpen,
|
||||
onClose,
|
||||
onPersonaModified
|
||||
onPersonaPreview
|
||||
}: PersonaModificationModalProps) {
|
||||
const [isProcessing, setIsProcessing] = useState(false);
|
||||
|
||||
|
|
@ -81,8 +81,8 @@ export default function PersonaModificationModal({
|
|||
setIsProcessing(true);
|
||||
|
||||
try {
|
||||
toastService.info("Modifying persona with AI...", {
|
||||
description: `Using ${values.llm_model} to process your modification request`
|
||||
toastService.info("Generating persona preview...", {
|
||||
description: `Using ${values.llm_model} to create a preview of your modifications`
|
||||
});
|
||||
|
||||
// Use the persona's MongoDB _id or fallback to id
|
||||
|
|
@ -92,26 +92,27 @@ export default function PersonaModificationModal({
|
|||
modification_prompt: values.modificationPrompt,
|
||||
llm_model: values.llm_model,
|
||||
reasoning_effort: values.reasoning_effort || 'medium',
|
||||
verbosity: values.verbosity || 'medium'
|
||||
verbosity: values.verbosity || 'medium',
|
||||
preview_only: true
|
||||
});
|
||||
|
||||
if (response.data && response.data.persona) {
|
||||
toastService.success("Persona modified successfully!", {
|
||||
description: `${persona.name} has been updated with AI modifications`
|
||||
toastService.success("Preview generated successfully!", {
|
||||
description: `Ready to review proposed changes to ${persona.name}`
|
||||
});
|
||||
|
||||
onPersonaModified(response.data.persona);
|
||||
onPersonaPreview(response.data.persona);
|
||||
handleClose();
|
||||
} else {
|
||||
throw new Error("Invalid response from server");
|
||||
}
|
||||
|
||||
} catch (error: any) {
|
||||
console.error("Error modifying persona:", error);
|
||||
console.error("Error generating persona preview:", error);
|
||||
|
||||
if (error.response) {
|
||||
const errorMessage = error.response.data?.error || "Server error occurred";
|
||||
toastService.error("Failed to modify persona", {
|
||||
toastService.error("Failed to generate preview", {
|
||||
description: errorMessage
|
||||
});
|
||||
} else if (error.request) {
|
||||
|
|
@ -119,7 +120,7 @@ export default function PersonaModificationModal({
|
|||
description: "Unable to connect to the server"
|
||||
});
|
||||
} else {
|
||||
toastService.error("Modification failed", {
|
||||
toastService.error("Preview generation failed", {
|
||||
description: error.message || "An unexpected error occurred"
|
||||
});
|
||||
}
|
||||
|
|
@ -283,12 +284,12 @@ export default function PersonaModificationModal({
|
|||
{isProcessing ? (
|
||||
<>
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
Processing...
|
||||
Generating Preview...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Wand2 className="h-4 w-4" />
|
||||
Process Persona Modification
|
||||
Generate Preview
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import Navigation from '@/components/Navigation';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
|
|
@ -30,12 +30,17 @@ import { usePersonaDetails } from '@/hooks/usePersonaDetails';
|
|||
export default function PersonaProfile() {
|
||||
const {
|
||||
currentPersona,
|
||||
displayPersona,
|
||||
isEditing,
|
||||
isFromReview,
|
||||
isLoading,
|
||||
isReviewMode,
|
||||
setIsEditing,
|
||||
handleGoBack,
|
||||
handleSaveEdit
|
||||
handleSaveEdit,
|
||||
enterReviewMode,
|
||||
exitReviewMode,
|
||||
saveReviewedPersona
|
||||
} = usePersonaDetails();
|
||||
|
||||
const { navigationState } = useNavigation();
|
||||
|
|
@ -43,6 +48,25 @@ export default function PersonaProfile() {
|
|||
const [isExporting, setIsExporting] = useState(false);
|
||||
const [isModificationModalOpen, setIsModificationModalOpen] = useState(false);
|
||||
|
||||
// Navigation blocking during review mode
|
||||
useEffect(() => {
|
||||
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
|
||||
if (isReviewMode) {
|
||||
e.preventDefault();
|
||||
e.returnValue = "You have unsaved changes. Your modifications will be lost if you leave.";
|
||||
return e.returnValue;
|
||||
}
|
||||
};
|
||||
|
||||
if (isReviewMode) {
|
||||
window.addEventListener('beforeunload', handleBeforeUnload);
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
};
|
||||
}, [isReviewMode]);
|
||||
|
||||
// Fetch focus group name if coming from focus group session
|
||||
useEffect(() => {
|
||||
if (navigationState.focusGroupId && navigationState.previousRoute?.startsWith('/focus-groups/')) {
|
||||
|
|
@ -196,43 +220,85 @@ export default function PersonaProfile() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Review Mode Banner */}
|
||||
{isReviewMode && (
|
||||
<div className="mb-6 p-4 bg-amber-50 border-l-4 border-amber-400 rounded-r-lg">
|
||||
<div className="flex items-center">
|
||||
<div className="ml-3">
|
||||
<p className="text-sm text-amber-800">
|
||||
📝 <strong>Reviewing proposed changes to {displayPersona?.name}</strong> - Save or cancel to continue
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex items-center mb-6 relative">
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={handleGoBack}
|
||||
onClick={() => {
|
||||
if (isReviewMode) {
|
||||
if (confirm("You have unsaved changes. Your modifications will be lost if you leave. Do you want to continue?")) {
|
||||
exitReviewMode();
|
||||
handleGoBack();
|
||||
}
|
||||
} else {
|
||||
handleGoBack();
|
||||
}
|
||||
}}
|
||||
className="absolute left-0 top-0 flex items-center"
|
||||
>
|
||||
<ArrowLeft className="h-5 w-5" />
|
||||
</Button>
|
||||
<h1 className="font-sf text-3xl font-bold text-slate-900 ml-16">Persona Profile</h1>
|
||||
<div className="absolute right-0 top-0 flex items-center gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleExportProfile}
|
||||
disabled={isExporting}
|
||||
className="hover-transition"
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
{isExporting ? 'Generating...' : 'Download Profile'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsModificationModalOpen(true)}
|
||||
className="hover-transition"
|
||||
>
|
||||
<Bot className="h-4 w-4 mr-2" />
|
||||
Modify with AI
|
||||
</Button>
|
||||
<Button onClick={() => setIsEditing(true)}>
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
Edit Persona
|
||||
</Button>
|
||||
{isReviewMode ? (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={exitReviewMode}
|
||||
className="hover-transition"
|
||||
>
|
||||
Cancel Revision
|
||||
</Button>
|
||||
<Button
|
||||
onClick={saveReviewedPersona}
|
||||
className="bg-green-600 hover:bg-green-700 text-white"
|
||||
>
|
||||
Save Revised Persona
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleExportProfile}
|
||||
disabled={isExporting}
|
||||
className="hover-transition"
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
{isExporting ? 'Generating...' : 'Download Profile'}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsModificationModalOpen(true)}
|
||||
className="hover-transition"
|
||||
>
|
||||
<Bot className="h-4 w-4 mr-2" />
|
||||
Modify with AI
|
||||
</Button>
|
||||
<Button onClick={() => setIsEditing(true)}>
|
||||
<Edit className="h-4 w-4 mr-2" />
|
||||
Edit Persona
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mt-10">
|
||||
<div className={`grid grid-cols-1 lg:grid-cols-3 gap-6 mt-10 ${isReviewMode ? 'border-2 border-amber-400 rounded-lg p-4 bg-amber-50/30' : ''}`}>
|
||||
<div className="lg:col-span-1">
|
||||
<PersonaSidebar persona={currentPersona} />
|
||||
<PersonaSidebar persona={displayPersona} />
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-2">
|
||||
|
|
@ -241,23 +307,23 @@ export default function PersonaProfile() {
|
|||
<TabsTrigger value="cooper-profile">Cooper Profile</TabsTrigger>
|
||||
<TabsTrigger value="personality">Personality</TabsTrigger>
|
||||
<TabsTrigger value="scenarios">Scenarios</TabsTrigger>
|
||||
<TabsTrigger value="generation-prompts">Generation Prompts</TabsTrigger>
|
||||
<TabsTrigger value="generation-prompts">Persona Inputs</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cooper-profile" className="mt-6">
|
||||
<PersonaCooperProfile persona={currentPersona} />
|
||||
<PersonaCooperProfile persona={displayPersona} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="personality" className="mt-6">
|
||||
<PersonaPersonality persona={currentPersona} />
|
||||
<PersonaPersonality persona={displayPersona} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="scenarios" className="mt-6">
|
||||
<PersonaScenarios persona={currentPersona} />
|
||||
<PersonaScenarios persona={displayPersona} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="generation-prompts" className="mt-6">
|
||||
<PersonaGenerationPrompts persona={currentPersona} />
|
||||
<PersonaGenerationPrompts persona={displayPersona} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
@ -271,10 +337,9 @@ export default function PersonaProfile() {
|
|||
persona={currentPersona}
|
||||
isOpen={isModificationModalOpen}
|
||||
onClose={() => setIsModificationModalOpen(false)}
|
||||
onPersonaModified={(modifiedPersona) => {
|
||||
// Update the current persona with the modified data
|
||||
// This will refresh the persona detail view
|
||||
window.location.reload();
|
||||
onPersonaPreview={(modifiedPersona) => {
|
||||
// Enter review mode with the modified persona data
|
||||
enterReviewMode(modifiedPersona);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -552,8 +552,10 @@ export function usePersonaDetails() {
|
|||
const navigate = useNavigate();
|
||||
const { navigationState, clearNavigationState } = useNavigation();
|
||||
const [currentPersona, setCurrentPersona] = useState<Persona | undefined>(undefined);
|
||||
const [reviewPersona, setReviewPersona] = useState<Persona | undefined>(undefined);
|
||||
const [isFromReview, setIsFromReview] = useState(false);
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [isReviewMode, setIsReviewMode] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -729,14 +731,63 @@ export function usePersonaDetails() {
|
|||
// If we got here, the save was successful
|
||||
return true;
|
||||
};
|
||||
|
||||
const enterReviewMode = (modifiedPersona: Persona) => {
|
||||
setReviewPersona(modifiedPersona);
|
||||
setIsReviewMode(true);
|
||||
};
|
||||
|
||||
const exitReviewMode = () => {
|
||||
setReviewPersona(undefined);
|
||||
setIsReviewMode(false);
|
||||
};
|
||||
|
||||
const saveReviewedPersona = async () => {
|
||||
if (!reviewPersona || !currentPersona) return false;
|
||||
|
||||
try {
|
||||
// Use the persona's MongoDB _id or fallback to id
|
||||
const personaId = currentPersona._id || currentPersona.id;
|
||||
|
||||
// Use the update API since we already have the modified data
|
||||
const updateResponse = await personasApi.update(personaId, reviewPersona);
|
||||
|
||||
if (updateResponse) {
|
||||
toast.success("Persona saved successfully!");
|
||||
setCurrentPersona(reviewPersona);
|
||||
exitReviewMode();
|
||||
// Give time for state update to complete before reloading
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 100);
|
||||
return true;
|
||||
} else {
|
||||
toast.error("Failed to save persona changes");
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error saving reviewed persona:", error);
|
||||
toast.error("Failed to save persona changes: " + (error.message || "Unknown error"));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the persona to display (review persona if in review mode, otherwise current persona)
|
||||
const displayPersona = isReviewMode ? reviewPersona : currentPersona;
|
||||
|
||||
return {
|
||||
currentPersona,
|
||||
displayPersona,
|
||||
isEditing,
|
||||
isFromReview,
|
||||
isLoading,
|
||||
isReviewMode,
|
||||
reviewPersona,
|
||||
setIsEditing,
|
||||
handleGoBack,
|
||||
handleSaveEdit
|
||||
handleSaveEdit,
|
||||
enterReviewMode,
|
||||
exitReviewMode,
|
||||
saveReviewedPersona
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,7 +172,8 @@ export const personasApi = {
|
|||
|
||||
modifyWithAI: (id: string, modificationData: any) => {
|
||||
const personaId = typeof id === 'object' && id !== null ? (id as any)._id || id : id;
|
||||
console.log(`Modifying persona with AI, ID: ${personaId}`);
|
||||
const mode = modificationData.preview_only ? 'Previewing' : 'Modifying';
|
||||
console.log(`${mode} persona with AI, ID: ${personaId}`);
|
||||
return api.post(`/personas/${personaId}/modify-with-ai`, modificationData);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -2283,7 +2283,7 @@ const FocusGroupSession = () => {
|
|||
<DialogHeader>
|
||||
<DialogTitle>AI Model Settings</DialogTitle>
|
||||
<DialogDescription>
|
||||
Choose which AI model to use for generating responses and discussion guides in this focus group.
|
||||
Choose which AI model to use for generating responses, discussion guides, and thematic analysis in this focus group.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue