Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,26 @@ import { SnackbarInsert } from '~/common/components/snackbar/SnackbarInsert';
import { hasGoogleAnalytics, OptionalGoogleAnalytics } from '~/common/components/3rdparty/GoogleAnalytics';
import { hasPostHogAnalytics, OptionalPostHogAnalytics } from '~/common/components/3rdparty/PostHogAnalytics';

import { SystemPurposes } from 'src/data';

const Big_AGI_App = ({ Component, emotionCache, pageProps }: MyAppProps) => {

React.useEffect(() => {
const storedPersonas = localStorage.getItem('personas');
if (storedPersonas) {
try {
const parsedPersonas = JSON.parse(storedPersonas);
// Update SystemPurposes with the loaded personas
Object.keys(parsedPersonas).forEach(key => {
SystemPurposes[key as any] = parsedPersonas[key];
});
console.log('Loaded personas from local storage:', SystemPurposes);
} catch (error) {
console.error('Error loading personas from local storage:', error);
}
}
}, []);

// We are using a nextjs per-page layout pattern to bring the (Optima) layout creation to a shared place
// This reduces the flicker and the time switching between apps, and seems to not have impact on
// the build. This is a good trade-off for now.
Expand Down
8 changes: 8 additions & 0 deletions src/apps/personas/creator/Creator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { PersonaForm } from './PersonaForm';

import { Alert, Box, Button, Card, CardContent, CircularProgress, Divider, FormLabel, Grid, IconButton, LinearProgress, Tab, tabClasses, TabList, TabPanel, Tabs, Typography } from '@mui/joy';
import AddIcon from '@mui/icons-material/Add';
Expand Down Expand Up @@ -218,12 +219,19 @@ export function Creator(props: { display: boolean }) {
>
<Tab>From YouTube</Tab>
<Tab>From Text</Tab>
<Tab>From Form</Tab>
</TabList>
<TabPanel keepMounted value={0} sx={{ p: 3 }}>
<FromYouTube isTransforming={isTransforming} onCreate={handleCreate} />
</TabPanel>
<TabPanel keepMounted value={1} sx={{ p: 3 }}>
<FromText isCreating={isTransforming} onCreate={handleCreate} />
</TabPanel>
<TabPanel keepMounted value={2} sx={{ p: 3 }}>
<PersonaForm />
</TabPanel>
<TabPanel keepMounted value={2} sx={{ p: 3 }}>
{/* Form for creating new personas */}
</TabPanel>

<Divider orientation='horizontal' />
Expand Down
107 changes: 107 additions & 0 deletions src/apps/personas/creator/PersonaForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as React from 'react';

import { Box, Button, FormControl, FormLabel, Input, Textarea } from '@mui/joy';
import { agiUuidV4 } from '~/common/util/idUtils';
import { SystemPurposes } from 'src/data';

export function PersonaForm() {
const [title, setTitle] = React.useState('');
const [description, setDescription] = React.useState('');
const [systemMessage, setSystemMessage] = React.useState('');
const [symbol, setSymbol] = React.useState('');
const [examples, setExamples] = React.useState(['']);

const handleAddExample = () => {
if (examples.length < 4) {
setExamples([...examples, '']);
}
};

const handleExampleChange = (index: number, value: string) => {
const newExamples = [...examples];
newExamples[index] = value;
setExamples(newExamples);
};

const handleSubmit = () => {
const newPersonaId = agiUuidV4('persona-2');

const newPersona = {
title,
description,
systemMessage,
symbol,
examples: examples.map(example => example),
};

SystemPurposes[newPersonaId as any] = newPersona;

localStorage.setItem('personas', JSON.stringify(SystemPurposes));

console.log('New persona added:', { newPersonaId, newPersona });
console.log('SystemPurposes:', SystemPurposes);
};

return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
<FormControl>
<FormLabel>Title</FormLabel>
<Input
type="text"
placeholder="Code Reviewer"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</FormControl>
<FormControl>
<FormLabel>Description</FormLabel>
<Textarea
placeholder="Helps you review code"
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</FormControl>
<FormControl>
<FormLabel>System Prompt</FormLabel>
<Textarea
placeholder="You are a code reviewer. You provide feedback on code quality, style, and best practices."
value={systemMessage}
onChange={(e) => setSystemMessage(e.target.value)}
/>
</FormControl>
<FormControl>
<FormLabel>Symbol</FormLabel>
<Input
type="text"
placeholder="🧐"
value={symbol}
onChange={(e) => setSymbol(e.target.value)}
/>
</FormControl>
<FormControl>
<FormLabel>Examples</FormLabel>
{examples.map((example, index) => (
<Box key={index} sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Input
type="text"
placeholder={index === 0 ? 'Check for potential bugs' : index === 1 ? 'Suggest improvements to code style' : 'Identify security vulnerabilities'}
value={example}
onChange={(e) => handleExampleChange(index, e.target.value)}
sx={{ mb: 1, flexGrow: 1 }}
/>
<Button onClick={() => {
const newExamples = [...examples];
newExamples.splice(index, 1);
setExamples(newExamples);
}}
sx={{ mb: 1 }}
>Remove</Button>
</Box>
))}
<br />
<Button onClick={handleAddExample}>Add Example</Button>
</FormControl>
<Button onClick={handleSubmit}>Create Persona</Button>
</Box>
);
}
6 changes: 5 additions & 1 deletion src/apps/personas/creator/Viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as React from 'react';
import TimeAgo from 'react-timeago';

import { Typography } from '@mui/joy';
import { Typography, Button } from '@mui/joy';
import { SystemPurposes } from 'src/data';

import { Link } from '~/common/components/Link';
import { useUIContentScaling } from '~/common/stores/store-ui';
Expand Down Expand Up @@ -37,5 +38,8 @@ export function Viewer(props: { selectedSimplePersonaId: string }) {
{simplePersona.inputProvenance?.type === 'text' && <>The source was a text snippet of {simplePersona.inputText?.length.toLocaleString()} characters.</>}
</Typography>

<Button onClick={() => {
delete SystemPurposes[props.selectedSimplePersonaId];
}}>Delete</Button>
</>;
}
3 changes: 2 additions & 1 deletion src/common/util/idUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type UidScope =
| 'livefile-item'
| 'logger'
| 'persona-creator-chain'
| 'persona'
| 'persona-simple'
| 'processing-queue-task'
| 'server-storage-deletion-key'
Expand All @@ -33,7 +34,7 @@ type UidScope =
* Application-wide unique identifier generator
* @param _scope Does not influence the ID generation, but is used to index all the IDs in the application
*/
export function agiUuid(_scope: Exclude<UidScope, 'chat-dfragment'>) {
export function agiUuid(_scope: Exclude<UidScope, 'chat-dfragment' | 'persona'>) {
return nanoid();
}

Expand Down
4 changes: 3 additions & 1 deletion src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export type SystemPurposeData = {

export type SystemPurposeExample = string | { prompt: string, action?: 'require-data-attachment' };

export const SystemPurposes: { [key in SystemPurposeId]: SystemPurposeData } = {
export type SystemPurposesType = { [key in SystemPurposeId]: SystemPurposeData; } & { [key: string]: SystemPurposeData };

export const SystemPurposes: SystemPurposesType = {
Generic: {
title: 'Default',
description: 'Start here',
Expand Down