diff --git a/src/apps/chat/components/persona-selector/PersonaSelector.tsx b/src/apps/chat/components/persona-selector/PersonaSelector.tsx index 523e0cf8c..f0cbc906f 100644 --- a/src/apps/chat/components/persona-selector/PersonaSelector.tsx +++ b/src/apps/chat/components/persona-selector/PersonaSelector.tsx @@ -3,19 +3,20 @@ import { shallow } from 'zustand/shallow'; import { Box, Button, Checkbox, Grid, IconButton, Input, Stack, Textarea, Typography } from '@mui/joy'; import ClearIcon from '@mui/icons-material/Clear'; -import ScienceIcon from '@mui/icons-material/Science'; import SearchIcon from '@mui/icons-material/Search'; import TelegramIcon from '@mui/icons-material/Telegram'; import { DConversationId, useChatStore } from '~/common/state/store-chats'; -import { Link } from '~/common/components/Link'; +import { navigateToPersonas } from '~/common/app.routes'; import { useUIPreferencesStore } from '~/common/state/store-ui'; -import { useUXLabsStore } from '~/common/state/store-ux-labs'; import { SystemPurposeId, SystemPurposes } from '../../../../data'; import { usePurposeStore } from './store-purposes'; +// 'special' purpose IDs, for tile hiding purposes +const PURPOSE_ID_PERSONA_CREATOR = '__persona-creator__'; + // Constants for tile sizes / grid width - breakpoints need to be computed here to work around // the "flex box cannot shrink over wrapped content" issue // @@ -47,7 +48,6 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa // external state const showFinder = useUIPreferencesStore(state => state.showPurposeFinder); - const labsPersonaYTCreator = useUXLabsStore(state => state.labsPersonaYTCreator); const { systemPurposeId, setSystemPurposeId } = useChatStore(state => { const conversation = state.conversations.find(conversation => conversation.id === props.conversationId); return { @@ -113,6 +113,8 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa const unfilteredPurposeIDs = (filteredIDs && showFinder) ? filteredIDs : Object.keys(SystemPurposes); const purposeIDs = editMode ? unfilteredPurposeIDs : unfilteredPurposeIDs.filter(id => !hiddenPurposeIDs.includes(id)); + const hidePersonaCreator = hiddenPurposeIDs.includes(PURPOSE_ID_PERSONA_CREATOR); + const selectedPurpose = purposeIDs.length ? (SystemPurposes[systemPurposeId] ?? null) : null; const selectedExample = selectedPurpose?.examples && getRandomElement(selectedPurpose.examples) || null; @@ -156,10 +158,14 @@ export function PersonaSelector(props: { conversationId: DConversationId, runExa ))} - {/* Button to start the YouTube persona creator */} - {labsPersonaYTCreator && + {/* Button to start the Persona Creator */} + {(editMode || !hidePersonaCreator) && } diff --git a/src/apps/personas/AppPersonas.tsx b/src/apps/personas/AppPersonas.tsx index f0d9c5c38..69454e867 100644 --- a/src/apps/personas/AppPersonas.tsx +++ b/src/apps/personas/AppPersonas.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { Box, Container, ListDivider, Sheet, Typography } from '@mui/joy'; -import { YTPersonaCreator } from './YTPersonaCreator'; +import { PersonaCreator } from './PersonaCreator'; import ScienceIcon from '@mui/icons-material/Science'; @@ -18,19 +18,12 @@ export function AppPersonas() { - Advanced AI Personas + AI Personas Creator - - - Experimental - - - - - + diff --git a/src/apps/personas/YTPersonaCreator.tsx b/src/apps/personas/PersonaCreator.tsx similarity index 55% rename from src/apps/personas/YTPersonaCreator.tsx rename to src/apps/personas/PersonaCreator.tsx index 272906df3..d5262b7a6 100644 --- a/src/apps/personas/YTPersonaCreator.tsx +++ b/src/apps/personas/PersonaCreator.tsx @@ -1,11 +1,13 @@ import * as React from 'react'; -import { Alert, Box, Button, Card, CardContent, CircularProgress, Grid, IconButton, Input, LinearProgress, Tooltip, Typography } from '@mui/joy'; +import { Alert, Box, Button, Card, CardContent, CircularProgress, Grid, Input, LinearProgress, Tab, TabList, TabPanel, Tabs, Textarea, Typography } from '@mui/joy'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; -import WhatshotIcon from '@mui/icons-material/Whatshot'; +import SettingsAccessibilityIcon from '@mui/icons-material/SettingsAccessibility'; +import TextFieldsIcon from '@mui/icons-material/TextFields'; import YouTubeIcon from '@mui/icons-material/YouTube'; import { GoodModal } from '~/common/components/GoodModal'; +import { GoodTooltip } from '~/common/components/GoodTooltip'; import { apiQuery } from '~/common/util/trpc.client'; import { copyToClipboard } from '~/common/util/clipboardUtils'; import { useFormRadioLlmType } from '~/common/components/forms/useFormRadioLlmType'; @@ -37,9 +39,9 @@ function useTranscriptFromVideo(videoID: string | null) { } -const YouTubePersonaSteps: LLMChainStep[] = [ +const PersonaCreationSteps: LLMChainStep[] = [ { - name: 'Analyzing the transcript', + name: 'Analyzing the transcript / text', setSystem: 'You are skilled in analyzing and embodying diverse characters. You meticulously study transcripts to capture key attributes, draft comprehensive character sheets, and refine them for authenticity. Feel free to make assumptions without hedging, be concise and be creative.', addUserInput: true, addUser: 'Conduct comprehensive research on the provided transcript. Identify key characteristics of the speaker, including age, professional field, distinct personality traits, style of communication, narrative context, and self-awareness. Additionally, consider any unique aspects such as their use of humor, their cultural background, core values, passions, fears, personal history, and social interactions. Your output for this stage is an in-depth written analysis that exhibits an understanding of both the superficial and more profound aspects of the speaker\'s persona.', @@ -62,14 +64,15 @@ const YouTubePersonaSteps: LLMChainStep[] = [ ]; -export function YTPersonaCreator() { +export function PersonaCreator() { // state const [videoURL, setVideoURL] = React.useState(''); const [videoID, setVideoID] = React.useState(''); const [personaTranscript, setPersonaTranscript] = React.useState(null); + const [personaText, setPersonaText] = React.useState(''); // external state - const [diagramLlm, llmComponent] = useFormRadioLlmType(); + const [personaLlm, llmComponent] = useFormRadioLlmType('Persona Creation Model'); // fetch transcript when the Video ID is ready, then store it const { transcript, thumbnailUrl, title, isFetching, isError, error: transcriptError } = @@ -78,7 +81,7 @@ export function YTPersonaCreator() { // use the transformation sequence to create a persona const { isFinished, isTransforming, chainProgress, chainIntermediates, chainStepName, chainOutput, chainError, abortChain } = - useLLMChain(YouTubePersonaSteps, diagramLlm?.id, personaTranscript ?? undefined); + useLLMChain(PersonaCreationSteps, personaLlm?.id, personaTranscript ?? undefined); const handleVideoIdChange = (e: React.ChangeEvent) => setVideoURL(e.target.value); @@ -93,61 +96,87 @@ export function YTPersonaCreator() { } }; + // New handler for persona text change + const handlePersonaTextChange = (e: React.ChangeEvent) => { + setPersonaText(e.target.value); + }; + return <> - - - - YouTube -> AI persona - - + + Create the System Prompt of an AI Persona from YouTube or Text. + -
- - + + From YouTube Video + From Text + + + {/* YouTube URL inputs */} + + + } sx={{ mb: 3 }}> + YouTube -> Persona + + + + + + + + + + + + + + {/* Text area for users to paste copied text */} + + + } sx={{ mb: 3 }}> + Text -> Persona + + +