mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 06:00:15 -07:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 343c89074a | |||
| fc1306eb96 | |||
| 5e11575b6a | |||
| fb8791aff4 | |||
| 9353666154 | |||
| bcc31d5ab9 |
@@ -74,6 +74,11 @@ export function AppChat() {
|
||||
};
|
||||
}, shallow);
|
||||
|
||||
// [0 to 1] create a conversation if there's none active
|
||||
React.useEffect(() => {
|
||||
if (!activeConversationId)
|
||||
useChatStore.getState().conversations.length === 0 && useChatStore.getState().createConversation();
|
||||
}, [activeConversationId]);
|
||||
|
||||
const handleExecuteConversation = async (chatModeId: ChatModeId, conversationId: string, history: DMessage[]) => {
|
||||
const { chatLLMId } = useModelsStore.getState();
|
||||
|
||||
@@ -26,10 +26,10 @@ export function ChatDropdowns(props: {
|
||||
}), shallow);
|
||||
|
||||
const { zenMode } = useUIPreferencesStore(state => ({ zenMode: state.zenMode }), shallow);
|
||||
const { systemPurposeId, setSystemPurposeId } = useChatStore(state => {
|
||||
const { systemPurposeValue, setSystemPurposeId } = useChatStore(state => {
|
||||
const conversation = state.conversations.find(conversation => conversation.id === props.conversationId);
|
||||
return {
|
||||
systemPurposeId: conversation?.systemPurposeId ?? null,
|
||||
systemPurposeValue: conversation?.systemPurposeId ?? null,
|
||||
setSystemPurposeId: state.setSystemPurposeId,
|
||||
};
|
||||
}, shallow);
|
||||
@@ -82,12 +82,11 @@ export function ChatDropdowns(props: {
|
||||
/>
|
||||
|
||||
{/* Persona selector */}
|
||||
{systemPurposeId && (
|
||||
<AppBarDropdown
|
||||
items={SystemPurposes} showSymbols={zenMode !== 'cleaner'}
|
||||
value={systemPurposeId} onChange={handleSystemPurposeChange}
|
||||
/>
|
||||
)}
|
||||
<AppBarDropdown
|
||||
items={SystemPurposes} showSymbols={zenMode !== 'cleaner'}
|
||||
value={systemPurposeValue} onChange={handleSystemPurposeChange}
|
||||
placeholder='Personas …'
|
||||
/>
|
||||
|
||||
</>;
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export function ConversationItem(props: {
|
||||
|
||||
const handleDeleteCancel = () => setDeleteArmed(false);
|
||||
|
||||
const textSymbol = SystemPurposes[systemPurposeId]?.symbol || '❓';
|
||||
const textSymbol = (systemPurposeId && SystemPurposes[systemPurposeId]?.symbol) || '❓';
|
||||
const buttonSx: SxProps = { ml: 1, ...(props.isActive ? { color: 'white' } : {}) };
|
||||
|
||||
const progress = props.maxChatMessages ? 100 * messageCount / props.maxChatMessages : 0;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import * as React from 'react';
|
||||
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 { Box, Button, Grid, IconButton, Stack, Typography } from '@mui/joy';
|
||||
import ScienceIcon from '@mui/icons-material/Science';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import TelegramIcon from '@mui/icons-material/Telegram';
|
||||
|
||||
import { Link } from '~/common/components/Link';
|
||||
@@ -12,7 +10,6 @@ import { useChatStore } from '~/common/state/store-chats';
|
||||
import { useUIPreferencesStore } from '~/common/state/store-ui';
|
||||
|
||||
import { SystemPurposeId, SystemPurposes } from '../../../../data';
|
||||
import { usePurposeStore } from './store-purposes';
|
||||
|
||||
|
||||
// Constants for tile sizes / grid width - breakpoints need to be computed here to work around
|
||||
@@ -40,104 +37,34 @@ const getRandomElement = <T, >(array: T[]): T | undefined =>
|
||||
*/
|
||||
export function PersonaSelector(props: { conversationId: string, runExample: (example: string) => void }) {
|
||||
// state
|
||||
const [searchQuery, setSearchQuery] = React.useState('');
|
||||
const [filteredIDs, setFilteredIDs] = React.useState<SystemPurposeId[] | null>(null);
|
||||
const [editMode, setEditMode] = React.useState(false);
|
||||
// const [editMode, setEditMode] = React.useState(false);
|
||||
const editMode = false;
|
||||
|
||||
// external state
|
||||
const { experimentalLabs, showFinder } = useUIPreferencesStore(state => ({
|
||||
const { experimentalLabs } = useUIPreferencesStore(state => ({
|
||||
experimentalLabs: state.experimentalLabs,
|
||||
showFinder: state.showPurposeFinder,
|
||||
}), shallow);
|
||||
const { systemPurposeId, setSystemPurposeId } = useChatStore(state => {
|
||||
const conversation = state.conversations.find(conversation => conversation.id === props.conversationId);
|
||||
return {
|
||||
systemPurposeId: conversation ? conversation.systemPurposeId : null,
|
||||
setSystemPurposeId: conversation ? state.setSystemPurposeId : null,
|
||||
setSystemPurposeId: state.setSystemPurposeId,
|
||||
};
|
||||
}, shallow);
|
||||
const { hiddenPurposeIDs, toggleHiddenPurposeId } = usePurposeStore(state => ({ hiddenPurposeIDs: state.hiddenPurposeIDs, toggleHiddenPurposeId: state.toggleHiddenPurposeId }), shallow);
|
||||
|
||||
// safety check - shouldn't happen
|
||||
if (!systemPurposeId || !setSystemPurposeId)
|
||||
return null;
|
||||
|
||||
|
||||
const handleSearchClear = () => {
|
||||
setSearchQuery('');
|
||||
setFilteredIDs(null);
|
||||
};
|
||||
|
||||
const handleSearchOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const query = e.target.value;
|
||||
if (!query)
|
||||
return handleSearchClear();
|
||||
setSearchQuery(query);
|
||||
|
||||
// Filter results based on search term
|
||||
const ids = Object.keys(SystemPurposes)
|
||||
.filter(key => SystemPurposes.hasOwnProperty(key))
|
||||
.filter(key => {
|
||||
const purpose = SystemPurposes[key as SystemPurposeId];
|
||||
return purpose.title.toLowerCase().includes(query.toLowerCase())
|
||||
|| (typeof purpose.description === 'string' && purpose.description.toLowerCase().includes(query.toLowerCase()));
|
||||
});
|
||||
setFilteredIDs(ids as SystemPurposeId[]);
|
||||
|
||||
// If there's a search term, activate the first item
|
||||
if (ids.length && !ids.includes(systemPurposeId))
|
||||
handlePurposeChanged(ids[0] as SystemPurposeId);
|
||||
};
|
||||
|
||||
const handleSearchOnKeyDown = (e: React.KeyboardEvent<HTMLInputElement>): void => {
|
||||
if (e.key == 'Escape')
|
||||
handleSearchClear();
|
||||
};
|
||||
|
||||
|
||||
const toggleEditMode = () => setEditMode(!editMode);
|
||||
|
||||
|
||||
const handlePurposeChanged = (purposeId: SystemPurposeId | null) => {
|
||||
if (purposeId)
|
||||
setSystemPurposeId(props.conversationId, purposeId);
|
||||
};
|
||||
|
||||
const handleCustomSystemMessageChange = (v: React.ChangeEvent<HTMLTextAreaElement>): void => {
|
||||
// TODO: persist this change? Right now it's reset every time.
|
||||
// maybe we shall have a "save" button just save on a state to persist between sessions
|
||||
SystemPurposes['Custom'].systemMessage = v.target.value;
|
||||
};
|
||||
|
||||
|
||||
// we show them all if the filter is clear (null)
|
||||
const unfilteredPurposeIDs = (filteredIDs && showFinder) ? filteredIDs : Object.keys(SystemPurposes);
|
||||
const purposeIDs = editMode ? unfilteredPurposeIDs : unfilteredPurposeIDs.filter(id => !hiddenPurposeIDs.includes(id));
|
||||
const purposeIDs = Object.keys(SystemPurposes);
|
||||
|
||||
const selectedPurpose = purposeIDs.length ? (SystemPurposes[systemPurposeId] ?? null) : null;
|
||||
const selectedPurpose = (purposeIDs.length && systemPurposeId) ? (SystemPurposes[systemPurposeId] ?? null) : null;
|
||||
const selectedExample = selectedPurpose?.examples && getRandomElement(selectedPurpose.examples) || null;
|
||||
|
||||
return <>
|
||||
|
||||
{showFinder && <Box sx={{ p: 2 * tileSpacing }}>
|
||||
<Input
|
||||
fullWidth
|
||||
variant='outlined' color='neutral'
|
||||
value={searchQuery} onChange={handleSearchOnChange}
|
||||
onKeyDown={handleSearchOnKeyDown}
|
||||
placeholder='Search for purpose…'
|
||||
startDecorator={<SearchIcon />}
|
||||
endDecorator={searchQuery && (
|
||||
<IconButton variant='plain' color='neutral' onClick={handleSearchClear}>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
sx={{
|
||||
boxShadow: 'sm',
|
||||
}}
|
||||
/>
|
||||
</Box>}
|
||||
|
||||
<Stack direction='column' sx={{ minHeight: '60vh', justifyContent: 'center', alignItems: 'center' }}>
|
||||
|
||||
<Box sx={{ maxWidth: bpMaxWidth }}>
|
||||
@@ -146,9 +73,9 @@ export function PersonaSelector(props: { conversationId: string, runExample: (ex
|
||||
<Typography level='title-sm'>
|
||||
AI Persona
|
||||
</Typography>
|
||||
<Button variant='plain' color='neutral' size='sm' onClick={toggleEditMode}>
|
||||
{editMode ? 'Done' : 'Edit'}
|
||||
</Button>
|
||||
{/*<Button variant='plain' color='neutral' size='sm' onClick={toggleEditMode}>*/}
|
||||
{/* {editMode ? 'Done' : 'Edit'}*/}
|
||||
{/*</Button>*/}
|
||||
</Box>
|
||||
|
||||
<Grid container spacing={tileSpacing} sx={{ justifyContent: 'flex-start' }}>
|
||||
@@ -170,13 +97,13 @@ export function PersonaSelector(props: { conversationId: string, runExample: (ex
|
||||
} : {}),
|
||||
}}
|
||||
>
|
||||
{editMode && (
|
||||
<Checkbox
|
||||
label={<Typography level='body-sm'>show</Typography>}
|
||||
checked={!hiddenPurposeIDs.includes(spId)} onChange={() => toggleHiddenPurposeId(spId)}
|
||||
sx={{ alignSelf: 'flex-start' }}
|
||||
/>
|
||||
)}
|
||||
{/*{editMode && (*/}
|
||||
{/* <Checkbox*/}
|
||||
{/* label={<Typography level='body-sm'>show</Typography>}*/}
|
||||
{/* checked={!hiddenPurposeIDs.includes(spId)} onChange={() => toggleHiddenPurposeId(spId)}*/}
|
||||
{/* sx={{ alignSelf: 'flex-start' }}*/}
|
||||
{/* />*/}
|
||||
{/*)}*/}
|
||||
<div style={{ fontSize: '2rem' }}>
|
||||
{SystemPurposes[spId as SystemPurposeId]?.symbol}
|
||||
</div>
|
||||
@@ -238,21 +165,6 @@ export function PersonaSelector(props: { conversationId: string, runExample: (ex
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{systemPurposeId === 'Custom' && (
|
||||
<Textarea
|
||||
variant='outlined' autoFocus placeholder={'Craft your custom system message here…'}
|
||||
minRows={3}
|
||||
defaultValue={SystemPurposes['Custom']?.systemMessage} onChange={handleCustomSystemMessageChange}
|
||||
sx={{
|
||||
backgroundColor: 'background.level1',
|
||||
'&:focus-within': {
|
||||
backgroundColor: 'background.popup',
|
||||
},
|
||||
lineHeight: 1.75,
|
||||
mt: 1,
|
||||
}} />
|
||||
)}
|
||||
|
||||
</Box>
|
||||
|
||||
</Stack>
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
|
||||
interface PurposeStore {
|
||||
|
||||
// state
|
||||
hiddenPurposeIDs: string[];
|
||||
|
||||
// actions
|
||||
toggleHiddenPurposeId: (purposeId: string) => void;
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const usePurposeStore = create<PurposeStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
|
||||
// default state
|
||||
hiddenPurposeIDs: ['Designer'],
|
||||
|
||||
toggleHiddenPurposeId: (purposeId: string) => {
|
||||
set(state => {
|
||||
const hiddenPurposeIDs = state.hiddenPurposeIDs.includes(purposeId)
|
||||
? state.hiddenPurposeIDs.filter((id) => id !== purposeId)
|
||||
: [...state.hiddenPurposeIDs, purposeId];
|
||||
return {
|
||||
hiddenPurposeIDs,
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
}),
|
||||
{
|
||||
name: 'app-purpose',
|
||||
}),
|
||||
);
|
||||
@@ -1,7 +1,5 @@
|
||||
import { fileSave } from 'browser-fs-access';
|
||||
|
||||
import { defaultSystemPurposeId } from '../../../data';
|
||||
|
||||
import { DModelSource } from '~/modules/llms/llm.types';
|
||||
import { useModelsStore } from '~/modules/llms/store-llms';
|
||||
|
||||
@@ -56,7 +54,7 @@ function restoreDConversationFromJson(fileName: string, part: Partial<DConversat
|
||||
const restored: DConversation = {
|
||||
id: part.id,
|
||||
messages: part.messages,
|
||||
systemPurposeId: part.systemPurposeId || defaultSystemPurposeId,
|
||||
systemPurposeId: part.systemPurposeId || undefined,
|
||||
...(part.userTitle && { userTitle: part.userTitle }),
|
||||
...(part.autoTitle && { autoTitle: part.autoTitle }),
|
||||
tokenCount: part.tokenCount || 0,
|
||||
|
||||
@@ -14,9 +14,6 @@ import { isPwa } from '~/common/util/pwaUtils';
|
||||
import { useUIPreferencesStore, useUIStateStore } from '~/common/state/store-ui';
|
||||
|
||||
|
||||
// configuration
|
||||
const SHOW_PURPOSE_FINDER = false;
|
||||
|
||||
|
||||
export function UISettings() {
|
||||
// external state
|
||||
@@ -26,7 +23,6 @@ export function UISettings() {
|
||||
enterToSend, setEnterToSend,
|
||||
experimentalLabs, setExperimentalLabs,
|
||||
renderMarkdown, setRenderMarkdown,
|
||||
showPurposeFinder, setShowPurposeFinder,
|
||||
zenMode, setZenMode,
|
||||
} = useUIPreferencesStore(state => ({
|
||||
centerMode: state.centerMode, setCenterMode: state.setCenterMode,
|
||||
@@ -34,7 +30,6 @@ export function UISettings() {
|
||||
enterToSend: state.enterToSend, setEnterToSend: state.setEnterToSend,
|
||||
experimentalLabs: state.experimentalLabs, setExperimentalLabs: state.setExperimentalLabs,
|
||||
renderMarkdown: state.renderMarkdown, setRenderMarkdown: state.setRenderMarkdown,
|
||||
showPurposeFinder: state.showPurposeFinder, setShowPurposeFinder: state.setShowPurposeFinder,
|
||||
zenMode: state.zenMode, setZenMode: state.setZenMode,
|
||||
}), shallow);
|
||||
const { closeSettings } = useUIStateStore(state => ({ closeSettings: state.closeSettings }), shallow);
|
||||
@@ -51,8 +46,6 @@ export function UISettings() {
|
||||
|
||||
const handleExperimentalLabsChange = (event: React.ChangeEvent<HTMLInputElement>) => setExperimentalLabs(event.target.checked);
|
||||
|
||||
const handleShowSearchBarChange = (event: React.ChangeEvent<HTMLInputElement>) => setShowPurposeFinder(event.target.checked);
|
||||
|
||||
return (
|
||||
|
||||
<Stack direction='column' sx={{ gap: settingsGap }}>
|
||||
@@ -99,16 +92,6 @@ export function UISettings() {
|
||||
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }} />
|
||||
</FormControl>
|
||||
|
||||
{SHOW_PURPOSE_FINDER && <FormControl orientation='horizontal' sx={{ justifyContent: 'space-between' }}>
|
||||
<Box>
|
||||
<FormLabel>Purpose finder</FormLabel>
|
||||
<FormHelperText>{showPurposeFinder ? 'Show search bar' : 'Hide search bar'}</FormHelperText>
|
||||
</Box>
|
||||
<Switch checked={showPurposeFinder} onChange={handleShowSearchBarChange}
|
||||
endDecorator={showPurposeFinder ? 'On' : 'Off'}
|
||||
slotProps={{ endDecorator: { sx: { minWidth: 26 } } }} />
|
||||
</FormControl>}
|
||||
|
||||
<FormControl orientation='horizontal' sx={{ alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<Box>
|
||||
<FormLabel>Appearance</FormLabel>
|
||||
|
||||
@@ -5,8 +5,8 @@ import { v4 as uuidv4 } from 'uuid';
|
||||
import { DLLMId } from '~/modules/llms/llm.types';
|
||||
import { useModelsStore } from '~/modules/llms/store-llms';
|
||||
|
||||
import { SystemPurposeId } from '../../data';
|
||||
import { countModelTokens } from '../util/token-counter';
|
||||
import { defaultSystemPurposeId, SystemPurposeId } from '../../data';
|
||||
|
||||
|
||||
// configuration
|
||||
@@ -22,7 +22,7 @@ export const MAX_CONVERSATIONS = 20;
|
||||
export interface DConversation {
|
||||
id: string;
|
||||
messages: DMessage[];
|
||||
systemPurposeId: SystemPurposeId;
|
||||
systemPurposeId: SystemPurposeId | undefined;
|
||||
userTitle?: string;
|
||||
autoTitle?: string;
|
||||
tokenCount: number; // f(messages, llmId)
|
||||
@@ -37,7 +37,7 @@ export function createDConversation(systemPurposeId?: SystemPurposeId): DConvers
|
||||
return {
|
||||
id: uuidv4(),
|
||||
messages: [],
|
||||
systemPurposeId: systemPurposeId || defaultSystemPurposeId,
|
||||
systemPurposeId: systemPurposeId || undefined,
|
||||
tokenCount: 0,
|
||||
created: Date.now(),
|
||||
updated: Date.now(),
|
||||
@@ -46,8 +46,6 @@ export function createDConversation(systemPurposeId?: SystemPurposeId): DConvers
|
||||
};
|
||||
}
|
||||
|
||||
const defaultConversations: DConversation[] = [createDConversation()];
|
||||
|
||||
/**
|
||||
* Message, sent or received, by humans or bots
|
||||
*
|
||||
@@ -148,8 +146,8 @@ export const useChatStore = create<ChatStore>()(devtools(
|
||||
(set, get) => ({
|
||||
|
||||
// default state
|
||||
conversations: defaultConversations,
|
||||
activeConversationId: defaultConversations[0].id,
|
||||
conversations: [],
|
||||
activeConversationId: null,
|
||||
|
||||
|
||||
createConversation: () =>
|
||||
|
||||
@@ -63,9 +63,6 @@ interface UIPreferencesStore {
|
||||
renderMarkdown: boolean;
|
||||
setRenderMarkdown: (renderMarkdown: boolean) => void;
|
||||
|
||||
showPurposeFinder: boolean;
|
||||
setShowPurposeFinder: (showPurposeFinder: boolean) => void;
|
||||
|
||||
showSystemMessages: boolean;
|
||||
setShowSystemMessages: (showSystemMessages: boolean) => void;
|
||||
|
||||
@@ -96,9 +93,6 @@ export const useUIPreferencesStore = create<UIPreferencesStore>()(
|
||||
renderMarkdown: false,
|
||||
setRenderMarkdown: (renderMarkdown: boolean) => set({ renderMarkdown }),
|
||||
|
||||
showPurposeFinder: false,
|
||||
setShowPurposeFinder: (showPurposeFinder: boolean) => set({ showPurposeFinder }),
|
||||
|
||||
showSystemMessages: false,
|
||||
setShowSystemMessages: (showSystemMessages: boolean) => set({ showSystemMessages }),
|
||||
|
||||
|
||||
+1
-9
@@ -1,8 +1,6 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export type SystemPurposeId = 'Catalyst' | 'Custom' | 'Designer' | 'Developer' | 'Executive' | 'Generic' | 'Scientist';
|
||||
|
||||
export const defaultSystemPurposeId: SystemPurposeId = 'Generic';
|
||||
export type SystemPurposeId = 'Catalyst' | 'Designer' | 'Developer' | 'Executive' | 'Generic' | 'Scientist';
|
||||
|
||||
type SystemPurposeData = {
|
||||
title: string;
|
||||
@@ -58,10 +56,4 @@ export const SystemPurposes: { [key in SystemPurposeId]: SystemPurposeData } = {
|
||||
symbol: '🧠',
|
||||
examples: ['help me plan a trip to Japan', 'what is the meaning of life?', 'how do I get a job at OpenAI?', 'what are some healthy meal ideas?'],
|
||||
},
|
||||
Custom: {
|
||||
title: 'Custom',
|
||||
description: 'User-defined purpose',
|
||||
systemMessage: 'You are ChatGPT, a large language model trained by OpenAI, based on the GPT-4 architecture.\nCurrent date: {{Today}}',
|
||||
symbol: '✨',
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user