mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-11 06:00:15 -07:00
Cleanup SendMode
This commit is contained in:
@@ -17,7 +17,6 @@ import { conversationToMarkdown } from '~/common/util/conversationToMarkdown';
|
||||
import { createDMessage, DMessage, restoreConversationFromJson, useChatStore } from '~/common/state/store-chats';
|
||||
import { extractCommands } from '~/common/util/extractCommands';
|
||||
import { useApplicationBarStore } from '~/common/layouts/appbar/store-applicationbar';
|
||||
import { useComposerStore } from '~/common/state/store-composer';
|
||||
import { useSettingsStore } from '~/common/state/store-settings';
|
||||
|
||||
import { ActionItems } from './components/appbar/ActionItems';
|
||||
@@ -34,6 +33,8 @@ import { runReActUpdatingState } from './editors/react-tangent';
|
||||
|
||||
const SPECIAL_ID_ALL_CHATS = 'all-chats';
|
||||
|
||||
export type SendModeId = 'immediate' | 'react';
|
||||
|
||||
|
||||
export function Chat() {
|
||||
|
||||
@@ -48,9 +49,6 @@ export function Chat() {
|
||||
|
||||
// external state
|
||||
const theme = useTheme();
|
||||
const { sendModeId } = useComposerStore(state => ({
|
||||
sendModeId: state.sendModeId,
|
||||
}), shallow);
|
||||
const { activeConversationId, isConversationEmpty, conversationsCount, importConversation, deleteAllConversations, setMessages, systemPurposeId, setAutoTitle } = useChatStore(state => {
|
||||
const conversation = state.conversations.find(conversation => conversation.id === state.activeConversationId);
|
||||
return {
|
||||
@@ -66,7 +64,7 @@ export function Chat() {
|
||||
}, shallow);
|
||||
|
||||
|
||||
const handleExecuteConversation = async (conversationId: string, history: DMessage[]) => {
|
||||
const handleExecuteConversation = async (sendModeId: SendModeId, conversationId: string, history: DMessage[]) => {
|
||||
const { chatLLMId } = useModelsStore.getState();
|
||||
if (!conversationId || !chatLLMId) return;
|
||||
|
||||
@@ -110,10 +108,10 @@ export function Chat() {
|
||||
const _findConversation = (conversationId: string) =>
|
||||
conversationId ? useChatStore.getState().conversations.find(c => c.id === conversationId) ?? null : null;
|
||||
|
||||
const handleSendUserMessage = async (conversationId: string, userText: string) => {
|
||||
const handleSendUserMessage = async (sendModeId: SendModeId, conversationId: string, userText: string) => {
|
||||
const conversation = _findConversation(conversationId);
|
||||
if (conversation)
|
||||
return await handleExecuteConversation(conversationId, [...conversation.messages, createDMessage('user', userText)]);
|
||||
return await handleExecuteConversation(sendModeId, conversationId, [...conversation.messages, createDMessage('user', userText)]);
|
||||
};
|
||||
|
||||
const handleImagineFromText = async (conversationId: string, messageText: string) => {
|
||||
@@ -121,7 +119,7 @@ export function Chat() {
|
||||
if (conversation) {
|
||||
const prompt = await imaginePromptFromText(messageText);
|
||||
if (prompt)
|
||||
return await handleExecuteConversation(conversationId, [...conversation.messages, createDMessage('user', `${CmdRunProdia[0]} ${prompt}`)]);
|
||||
return await handleExecuteConversation('immediate', conversationId, [...conversation.messages, createDMessage('user', `${CmdRunProdia[0]} ${prompt}`)]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,13 +11,20 @@ import { useSettingsStore } from '~/common/state/store-settings';
|
||||
|
||||
import { ChatMessage } from './message/ChatMessage';
|
||||
import { ChatMessageSelectable, MessagesSelectionHeader } from './message/ChatMessageSelectable';
|
||||
import { PurposeSelector } from './PurposeSelector';
|
||||
import { PurposeSelector } from './purpose-selector/PurposeSelector';
|
||||
import { SendModeId } from '../Chat';
|
||||
|
||||
|
||||
/**
|
||||
* A list of ChatMessages
|
||||
*/
|
||||
export function ChatMessageList(props: { conversationId: string | null, isMessageSelectionMode: boolean, setIsMessageSelectionMode: (isMessageSelectionMode: boolean) => void, onExecuteConversation: (conversationId: string, history: DMessage[]) => void, onImagineFromText: (conversationId: string, userText: string) => void, sx?: SxProps }) {
|
||||
export function ChatMessageList(props: {
|
||||
conversationId: string | null,
|
||||
isMessageSelectionMode: boolean, setIsMessageSelectionMode: (isMessageSelectionMode: boolean) => void,
|
||||
onExecuteConversation: (sendModeId: SendModeId, conversationId: string, history: DMessage[]) => void,
|
||||
onImagineFromText: (conversationId: string, userText: string) => void,
|
||||
sx?: SxProps
|
||||
}) {
|
||||
// state
|
||||
const [selectedMessages, setSelectedMessages] = React.useState<Set<string>>(new Set());
|
||||
|
||||
@@ -44,11 +51,11 @@ export function ChatMessageList(props: { conversationId: string | null, isMessag
|
||||
|
||||
const handleRestartFromMessage = (messageId: string, offset: number) => {
|
||||
const truncatedHistory = messages.slice(0, messages.findIndex(m => m.id === messageId) + offset + 1);
|
||||
props.conversationId && props.onExecuteConversation(props.conversationId, truncatedHistory);
|
||||
props.conversationId && props.onExecuteConversation('immediate', props.conversationId, truncatedHistory);
|
||||
};
|
||||
|
||||
const handleRunExample = (text: string) =>
|
||||
props.conversationId && props.onExecuteConversation(props.conversationId, [...messages, createDMessage('user', text)]);
|
||||
props.conversationId && props.onExecuteConversation('immediate', props.conversationId, [...messages, createDMessage('user', text)]);
|
||||
|
||||
|
||||
// hide system messages if the user chooses so
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { Box, Button, Card, Grid, IconButton, ListDivider, ListItemDecorator, Menu, MenuItem, Radio, Stack, Textarea, Tooltip, Typography, useTheme } from '@mui/joy';
|
||||
import { Box, Button, Card, Grid, IconButton, ListDivider, ListItemDecorator, Menu, MenuItem, Stack, Textarea, Tooltip, Typography, useTheme } from '@mui/joy';
|
||||
import { ColorPaletteProp, SxProps, VariantProp } from '@mui/joy/styles/types';
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import ContentPasteGoIcon from '@mui/icons-material/ContentPasteGo';
|
||||
@@ -20,19 +20,20 @@ import { ContentReducer } from '~/modules/aifn/summarize/ContentReducer';
|
||||
import { useChatLLM } from '~/modules/llms/store-llms';
|
||||
|
||||
import { ConfirmationModal } from '~/common/components/ConfirmationModal';
|
||||
import { SendModeId, SendModes } from '../../../../data';
|
||||
import { countModelTokens } from '~/common/llm-util/token-counter';
|
||||
import { extractFilePathsWithCommonRadix } from '~/common/util/dropTextUtils';
|
||||
import { hideOnDesktop, hideOnMobile } from '~/common/theme';
|
||||
import { htmlTableToMarkdown } from '~/common/util/htmlTableToMarkdown';
|
||||
import { pdfToText } from '~/common/util/pdfToText';
|
||||
import { useChatStore } from '~/common/state/store-chats';
|
||||
import { useComposerStore } from '~/common/state/store-composer';
|
||||
import { useSettingsStore } from '~/common/state/store-settings';
|
||||
import { useSpeechRecognition } from '~/common/components/useSpeechRecognition';
|
||||
|
||||
import { SendModeId } from '../../Chat';
|
||||
import { SendModeMenu } from './SendModeMenu';
|
||||
import { TokenBadge } from './TokenBadge';
|
||||
import { TokenProgressbar } from './TokenProgressbar';
|
||||
import { useComposerStore } from './composer.store';
|
||||
|
||||
|
||||
/// Text template helpers
|
||||
@@ -97,29 +98,6 @@ const MicButton = (props: { variant: VariantProp, color: ColorPaletteProp, onCli
|
||||
</Tooltip>;
|
||||
|
||||
|
||||
const SendModeMenu = (props: { anchorEl: HTMLAnchorElement, sendMode: SendModeId, onSetSendMode: (sendMode: SendModeId) => void, onClose: () => void, }) =>
|
||||
<Menu
|
||||
variant='plain' color='neutral' size='md' placement='top-end' sx={{ minWidth: 320, overflow: 'auto' }}
|
||||
open anchorEl={props.anchorEl} onClose={props.onClose}>
|
||||
|
||||
<MenuItem color='neutral' selected>Conversation Mode</MenuItem>
|
||||
|
||||
<ListDivider />
|
||||
|
||||
{Object.entries(SendModes).map(([key, data]) =>
|
||||
<MenuItem key={'send-mode-' + key} onClick={() => props.onSetSendMode(key as SendModeId)}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
|
||||
<Radio checked={key === props.sendMode} />
|
||||
<Box>
|
||||
<Typography>{data.label}</Typography>
|
||||
<Typography level='body2'>{data.description}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</MenuItem>)}
|
||||
|
||||
</Menu>;
|
||||
|
||||
|
||||
const SentMessagesMenu = (props: {
|
||||
anchorEl: HTMLAnchorElement, onClose: () => void,
|
||||
messages: { date: number; text: string; count: number }[],
|
||||
@@ -163,11 +141,12 @@ const SentMessagesMenu = (props: {
|
||||
export function Composer(props: {
|
||||
conversationId: string | null; messageId: string | null;
|
||||
isDeveloperMode: boolean;
|
||||
onSendMessage: (conversationId: string, text: string) => void;
|
||||
onSendMessage: (sendModeId: SendModeId, conversationId: string, text: string) => void;
|
||||
sx?: SxProps;
|
||||
}) {
|
||||
// state
|
||||
const [composeText, setComposeText] = React.useState('');
|
||||
const [sendModeId, setSendModeId] = React.useState<SendModeId>('immediate');
|
||||
const [isDragging, setIsDragging] = React.useState(false);
|
||||
const [reducerText, setReducerText] = React.useState('');
|
||||
const [reducerTextTokens, setReducerTextTokens] = React.useState(0);
|
||||
@@ -179,7 +158,7 @@ export function Composer(props: {
|
||||
// external state
|
||||
const theme = useTheme();
|
||||
const enterToSend = useSettingsStore(state => state.enterToSend);
|
||||
const { sendModeId, setSendModeId, sentMessages, appendSentMessage, clearSentMessages } = useComposerStore();
|
||||
const { sentMessages, appendSentMessage, clearSentMessages } = useComposerStore();
|
||||
const { assistantTyping, tokenCount: conversationTokenCount, stopTyping } = useChatStore(state => {
|
||||
const conversation = state.conversations.find(conversation => conversation.id === props.conversationId);
|
||||
return {
|
||||
@@ -204,7 +183,7 @@ export function Composer(props: {
|
||||
const text = (composeText || '').trim();
|
||||
if (text.length && props.conversationId) {
|
||||
setComposeText('');
|
||||
props.onSendMessage(props.conversationId, text);
|
||||
props.onSendMessage(sendModeId, props.conversationId, text);
|
||||
appendSentMessage(text);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Box, ListDivider, Menu, MenuItem, Radio, Typography } from '@mui/joy';
|
||||
|
||||
import { SendModeId } from '../../Chat';
|
||||
|
||||
|
||||
/// SendMode(s) definition
|
||||
|
||||
type SendModeData = {
|
||||
label: string;
|
||||
description: string | React.JSX.Element;
|
||||
}
|
||||
|
||||
const SendModeItems: { [key in SendModeId]: SendModeData } = {
|
||||
'immediate': {
|
||||
label: 'Chat',
|
||||
description: 'AI-powered direct responses',
|
||||
},
|
||||
'react': {
|
||||
label: 'Reason+Act',
|
||||
description: 'Answer your questions with ReAct and search',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export const SendModeMenu = (props: { anchorEl: HTMLAnchorElement, sendMode: SendModeId, onSetSendMode: (sendMode: SendModeId) => void, onClose: () => void, }) =>
|
||||
<Menu
|
||||
variant='plain' color='neutral' size='md' placement='top-end' sx={{ minWidth: 320, overflow: 'auto' }}
|
||||
open anchorEl={props.anchorEl} onClose={props.onClose}>
|
||||
|
||||
<MenuItem color='neutral' selected>Conversation Mode</MenuItem>
|
||||
|
||||
<ListDivider />
|
||||
|
||||
{Object.entries(SendModeItems).map(([key, data]) =>
|
||||
<MenuItem key={'send-mode-' + key} onClick={() => props.onSetSendMode(key as SendModeId)}>
|
||||
<Box sx={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 2 }}>
|
||||
<Radio checked={key === props.sendMode} />
|
||||
<Box>
|
||||
<Typography>{data.label}</Typography>
|
||||
<Typography level='body2'>{data.description}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</MenuItem>)}
|
||||
|
||||
</Menu>;
|
||||
+1
-8
@@ -1,15 +1,11 @@
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { defaultSendModeId, SendModeId } from '../../data';
|
||||
|
||||
|
||||
/// Composer Store
|
||||
|
||||
interface ComposerStore {
|
||||
|
||||
sendModeId: SendModeId;
|
||||
setSendModeId: (sendMode: SendModeId) => void;
|
||||
|
||||
sentMessages: {
|
||||
date: number,
|
||||
text: string,
|
||||
@@ -23,9 +19,6 @@ interface ComposerStore {
|
||||
export const useComposerStore = create<ComposerStore>()(
|
||||
persist((set, get) => ({
|
||||
|
||||
sendModeId: defaultSendModeId,
|
||||
setSendModeId: (sendMode: SendModeId) => set({ sendModeId: sendMode }),
|
||||
|
||||
sentMessages: [],
|
||||
appendSentMessage: (text: string) => {
|
||||
const date = Date.now();
|
||||
@@ -61,4 +54,4 @@ export const useComposerStore = create<ComposerStore>()(
|
||||
return state as ComposerStore;
|
||||
},
|
||||
}),
|
||||
);
|
||||
);
|
||||
+3
-2
@@ -5,11 +5,12 @@ import { Box, Button, Checkbox, Grid, IconButton, Input, Stack, Textarea, Typogr
|
||||
import ClearIcon from '@mui/icons-material/Clear';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
|
||||
import { SystemPurposeId, SystemPurposes } from '../../../data';
|
||||
import { useChatStore } from '~/common/state/store-chats';
|
||||
import { usePurposeStore } from '~/common/state/store-purposes';
|
||||
import { useSettingsStore } from '~/common/state/store-settings';
|
||||
|
||||
import { SystemPurposeId, SystemPurposes } from '../../../../data';
|
||||
import { usePurposeStore } from './purposes.store';
|
||||
|
||||
|
||||
// Constants for tile sizes / grid width - breakpoints need to be computed here to work around
|
||||
// the "flex box cannot shrink over wrapped content" issue
|
||||
-20
@@ -63,23 +63,3 @@ export const SystemPurposes: { [key in SystemPurposeId]: SystemPurposeData } = {
|
||||
symbol: '✨',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
export type SendModeId = 'immediate' | 'react';
|
||||
export const defaultSendModeId: SendModeId = 'immediate';
|
||||
|
||||
type SendModeData = {
|
||||
label: string;
|
||||
description: string | JSX.Element;
|
||||
}
|
||||
|
||||
export const SendModes: { [key in SendModeId]: SendModeData } = {
|
||||
'immediate': {
|
||||
label: 'Chat',
|
||||
description: 'AI-powered direct responses',
|
||||
},
|
||||
'react': {
|
||||
label: 'Reason+Act',
|
||||
description: 'Answer your questions with ReAct and search',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user