import React, { useState, useRef, useEffect, useCallback } from 'react'; import { Scenario, Message, AnalysisResult, UserSettings } from '../types'; import { BrainCircuit, ChevronLeft, Flag, Loader2 } from 'lucide-react'; import { streamOpenRouterChat, analyzeTurnWithOpenRouter, generateSessionSummaryReport } from '../services/openRouterService'; import { saveSession } from '../services/sessionStorage'; // Sub-components import ChatMessage from './ChatMessage'; import ChatInput from './ChatInput'; import LiveInsightPanel from './LiveInsightPanel'; import SessionReport from './SessionReport'; interface ChatSessionProps { scenario: Scenario; userSettings?: UserSettings; onExit: () => void; } const ChatSession: React.FC = ({ scenario, userSettings, onExit }) => { const [messages, setMessages] = useState([]); const [isLoading, setIsLoading] = useState(false); const [status, setStatus] = useState<'active' | 'analyzing' | 'finished'>('active'); // Clinical Feedback State const [liveInsight, setLiveInsight] = useState(null); const [isAnalyzingTurn, setIsAnalyzingTurn] = useState(false); const [finalReport, setFinalReport] = useState(null); const messagesEndRef = useRef(null); const scrollToBottom = useCallback(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, []); useEffect(() => { scrollToBottom(); }, [messages, scrollToBottom]); // Session Initialization useEffect(() => { // Initial Message setMessages([{ id: 'init', role: 'model', text: scenario.initialMessage, timestamp: new Date() }]); setLiveInsight(null); }, [scenario]); const handleSend = useCallback(async (text: string) => { if (isLoading || !text.trim()) return; const userMsg: Message = { id: Date.now().toString(), role: 'user', text: text, timestamp: new Date() }; const updatedHistory = [...messages, userMsg]; setMessages(updatedHistory); setIsLoading(true); const botMsgId = (Date.now() + 1).toString(); const botMsg: Message = { id: botMsgId, role: 'model', text: '', timestamp: new Date() }; setMessages([...updatedHistory, botMsg]); const roleplaySystemInstruction = ` ${scenario.systemInstruction} [CONTEXT] You are participating in a clinical CBT roleplay simulation. The instructions above define your character, the scenario, and your internal CBT logic. [TASK] Respond to the user naturally in character to advance the roleplay scenario. [CONSTRAINTS] - CRITICAL: Stay strictly in character as the persona defined in the scenario. - CRITICAL: NEVER provide clinical feedback, advice, or meta-commentary about the user's progress. - CRITICAL: NEVER use phrases like "Great job holding your boundary" or "Feedback:". - CRITICAL: Do not congratulate the user on using CBT techniques; simply react as the character would. - If the user successfully concludes the scenario, end the dialogue naturally. [OUTPUT REQUIREMENTS] Return the character's direct dialogue response. Keep it conversational and proportional to the user's input. `; try { let accumulatedText = ''; await streamOpenRouterChat( updatedHistory, roleplaySystemInstruction, { onChunk: (chunk) => { accumulatedText += chunk; setMessages(prev => prev.map(m => m.id === botMsgId ? { ...m, text: accumulatedText } : m )); }, onComplete: (fullText) => { // Live Turn Clinical Analysis via OpenRouter setIsAnalyzingTurn(true); const turnContext = `User said: "${userMsg.text}". Roleplay partner said: "${fullText}".`; analyzeTurnWithOpenRouter( turnContext, scenario.title, scenario.description, scenario.cbtFocus, { apiKey: userSettings?.openRouterApiKey, model: userSettings?.supervisorModel } ).then(res => { setLiveInsight(res); }).catch(e => { console.error("Live analysis failed", e); }).finally(() => { setIsAnalyzingTurn(false); }); }, onError: (err) => { console.error("OpenRouter stream failed", err); } }, { apiKey: userSettings?.openRouterApiKey, model: userSettings?.roleplayModel } ); } catch (error) { console.error("Chat transmission failed", error); setMessages(prev => prev.filter(m => m.text !== '')); } finally { setIsLoading(false); } }, [messages, scenario, isLoading, userSettings]); const handleFinishSession = async () => { if (messages.length < 2) return onExit(); setStatus('analyzing'); try { const reportData = await generateSessionSummaryReport( messages, scenario.title, scenario.description, { apiKey: userSettings?.openRouterApiKey, model: userSettings?.supervisorModel } ); setFinalReport(reportData); saveSession(scenario, 'text', messages, reportData); setStatus('finished'); } catch (err) { console.error("Post-session analysis failure", err); onExit(); } }; if (status === 'analyzing') { return (

Compiling Report

Generating evidence-based synthesis of your cognitive and behavioral patterns during this session via OpenRouter...

); } if (status === 'finished' && finalReport) { return ( ); } return (
{/* Interaction Header */}

{scenario.title}

{scenario.cbtFocus.slice(0, 3).map(f => ( {f} ))}
{/* Core Chat Module */}
{messages.map((msg) => ( ))}
{/* Real-time Insights Module */}
); }; export default React.memo(ChatSession);