import React, { useEffect, useState, useRef } from 'react'; import Box from '@mui/material/Box'; import Tooltip from '@mui/material/Tooltip'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; import CancelIcon from '@mui/icons-material/Cancel'; import SendIcon from '@mui/icons-material/Send'; import PropagateLoader from 'react-spinners/PropagateLoader'; import { CandidateInfo } from '../Components/CandidateInfo'; import { Query } from '../../Components/ChatQuery' import { streamQueryResponse, StreamQueryController } from '../Components/streamQueryResponse'; import { connectionBase } from 'Global'; import { UserInfo } from '../Components/UserContext'; import { BackstoryElementProps } from 'Components/BackstoryTab'; import { BackstoryTextField, BackstoryTextFieldRef } from 'Components/BackstoryTextField'; import { jsonrepair } from 'jsonrepair'; import { StyledMarkdown } from 'Components/StyledMarkdown'; import { Scrollable } from 'Components/Scrollable'; import { useForkRef } from '@mui/material'; const emptyUser : UserInfo = { type: 'candidate', description: "[blank]", rag_content_size: 0, username: "[blank]", first_name: "[blank]", last_name: "[blank]", full_name: "[blank] [blank]", contact_info: {}, questions: [], isAuthenticated: false, has_profile: false }; const GenerateCandidate = (props: BackstoryElementProps) => { const {sessionId, setSnack, submitQuery} = props; const [streaming, setStreaming] = useState(''); const [processing, setProcessing] = useState(false); const [user, setUser] = useState(emptyUser); const controllerRef = useRef(null); const backstoryTextRef = useRef(null); const promptRef = useRef(null); const stateRef = useRef(0); /* Generating persona */ const userRef = useRef(user); const [prompt, setPrompt] = useState(''); const [resume, setResume] = useState(''); const processQuery = (query: Query) => { if (controllerRef.current) { return; } setPrompt(query.prompt); promptRef.current = query.prompt; stateRef.current = 0; setUser(emptyUser); setStreaming(''); setResume(''); setProcessing(true); controllerRef.current = streamQueryResponse({ query, type: "persona", sessionId, connectionBase, onComplete: (msg) => { console.log({ msg, state: stateRef.current, prompt: promptRef.current || '' }); switch (msg.status) { case "partial": case "done": switch (stateRef.current) { case 0: /* Generating persona */ let partialUser = JSON.parse(jsonrepair((msg.response || '').trim())); if (!partialUser.full_name) { partialUser.full_name = `${partialUser.first_name} ${partialUser.last_name}`; } console.log(partialUser); setUser(partialUser); stateRef.current = 1 /* Generating resume */ break; case 1: /* Generating resume */ stateRef.current = 2 /* RAG generation */ break; case 2: /* RAG generation */ stateRef.current = 2 /* Image generation */ break; case 3: /* Generating image */ let imageGeneration = JSON.parse(jsonrepair((msg.response || '').trim())); console.log(imageGeneration); if (imageGeneration >= 100) { setUser({...userRef.current}); } else { setPrompt(imageGeneration.status); } stateRef.current = 3 /* ... */ } if (msg.status === "done") { setProcessing(false); controllerRef.current = null; stateRef.current = 0; } break; case "thinking": setPrompt(msg.response || ''); break; case "error": console.log(`Error generating persona: ${msg.response}`); setSnack(msg.response || "", "error"); setProcessing(false); setUser({...userRef.current}); controllerRef.current = null; stateRef.current = 0; break; } }, onStreaming: (chunk) => { setStreaming(chunk); } }); }; const cancelQuery = () => { if (controllerRef.current) { controllerRef.current.abort(); controllerRef.current = null; stateRef.current = 0; setProcessing(false); } } useEffect(() => { promptRef.current = prompt; }, [prompt]); useEffect(() => { userRef.current = user; }, [user]); useEffect(() => { if (streaming.trim().length === 0) { return; } try { switch (stateRef.current) { case 0: /* Generating persona */ const partialUser = {...emptyUser, ...JSON.parse(jsonrepair(`${streaming.trim()}...`))}; if (!partialUser.full_name) { partialUser.full_name = `${partialUser.first_name} ${partialUser.last_name}`; } setUser(partialUser); break; case 1: /* Generating resume */ setResume(streaming); break; case 3: /* RAG streaming */ break; case 4: /* Image streaming */ break; } } catch { } }, [streaming]); if (!sessionId) { return <>; } const onEnter = (value: string) => { if (processing) { return; } const query: Query = { prompt: value } processQuery(query); }; return (<> { user && } { resume !== '' && } {processing && Genearating {stateRef.current === 0 && "persona"} {stateRef.current === 1 && "resume"} {stateRef.current === 2 && "RAG"} {stateRef.current === 3 && "profile image"} :{prompt} } { /* This span is used to wrap the IconButton to ensure Tooltip works even when disabled */} { cancelQuery(); }} sx={{ display: "flex", margin: 'auto 0px' }} size="large" edge="start" disabled={controllerRef.current === null || !sessionId || processing === false} > ); }; export { GenerateCandidate };