Tell the user to hit compact (#3851)
Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
@@ -127,7 +127,7 @@ function BaseChatContent({
|
|||||||
summaryContent,
|
summaryContent,
|
||||||
summarizedThread,
|
summarizedThread,
|
||||||
isSummaryModalOpen,
|
isSummaryModalOpen,
|
||||||
isLoadingSummary,
|
isLoadingCompaction,
|
||||||
resetMessagesWithSummary,
|
resetMessagesWithSummary,
|
||||||
closeSummaryModal,
|
closeSummaryModal,
|
||||||
updateSummary,
|
updateSummary,
|
||||||
@@ -513,7 +513,7 @@ function BaseChatContent({
|
|||||||
{chatState !== ChatState.Idle && (
|
{chatState !== ChatState.Idle && (
|
||||||
<div className="absolute bottom-1 left-4 z-20 pointer-events-none">
|
<div className="absolute bottom-1 left-4 z-20 pointer-events-none">
|
||||||
<LoadingGoose
|
<LoadingGoose
|
||||||
message={isLoadingSummary ? 'summarizing conversation…' : undefined}
|
message={isLoadingCompaction ? 'summarizing conversation…' : undefined}
|
||||||
chatState={chatState}
|
chatState={chatState}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { Message } from '../types/message';
|
|||||||
import { DirSwitcher } from './bottom_menu/DirSwitcher';
|
import { DirSwitcher } from './bottom_menu/DirSwitcher';
|
||||||
import ModelsBottomBar from './settings/models/bottom_bar/ModelsBottomBar';
|
import ModelsBottomBar from './settings/models/bottom_bar/ModelsBottomBar';
|
||||||
import { BottomMenuModeSelection } from './bottom_menu/BottomMenuModeSelection';
|
import { BottomMenuModeSelection } from './bottom_menu/BottomMenuModeSelection';
|
||||||
import { ManualSummarizeButton } from './context_management/ManualSummaryButton';
|
import { ManualCompactButton } from './context_management/ManualCompactButton';
|
||||||
import { AlertType, useAlerts } from './alerts';
|
import { AlertType, useAlerts } from './alerts';
|
||||||
import { useToolCount } from './alerts/useToolCount';
|
import { useToolCount } from './alerts/useToolCount';
|
||||||
import { useConfig } from './ConfigContext';
|
import { useConfig } from './ConfigContext';
|
||||||
@@ -110,7 +110,7 @@ export default function ChatInput({
|
|||||||
const { alerts, addAlert, clearAlerts } = useAlerts();
|
const { alerts, addAlert, clearAlerts } = useAlerts();
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
const toolCount = useToolCount();
|
const toolCount = useToolCount();
|
||||||
const { isLoadingSummary } = useChatContextManager();
|
const { isLoadingCompaction } = useChatContextManager();
|
||||||
const { getProviders, read } = useConfig();
|
const { getProviders, read } = useConfig();
|
||||||
const { getCurrentModelAndProvider, currentModel, currentProvider } = useModelAndProvider();
|
const { getCurrentModelAndProvider, currentModel, currentProvider } = useModelAndProvider();
|
||||||
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
|
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
|
||||||
@@ -416,7 +416,7 @@ export default function ChatInput({
|
|||||||
// Only show warning alert when approaching limit
|
// Only show warning alert when approaching limit
|
||||||
addAlert({
|
addAlert({
|
||||||
type: AlertType.Warning,
|
type: AlertType.Warning,
|
||||||
message: `Approaching token limit (${numTokens.toLocaleString()}/${tokenLimit.toLocaleString()}) \n You're reaching the model's conversation limit. The session will be saved — copy anything important and start a new one to continue.`,
|
message: `Approaching token limit (${numTokens.toLocaleString()}/${tokenLimit.toLocaleString()}) \n You're reaching the model's conversation limit. Consider compacting the conversation to continue.`,
|
||||||
autoShow: true, // Auto-show token limit warnings
|
autoShow: true, // Auto-show token limit warnings
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -880,7 +880,7 @@ export default function ChatInput({
|
|||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
!isLoading &&
|
!isLoading &&
|
||||||
!isLoadingSummary &&
|
!isLoadingCompaction &&
|
||||||
(displayValue.trim() ||
|
(displayValue.trim() ||
|
||||||
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
||||||
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
||||||
@@ -894,7 +894,7 @@ export default function ChatInput({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const canSubmit =
|
const canSubmit =
|
||||||
!isLoading &&
|
!isLoading &&
|
||||||
!isLoadingSummary &&
|
!isLoadingCompaction &&
|
||||||
(displayValue.trim() ||
|
(displayValue.trim() ||
|
||||||
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
pastedImages.some((img) => img.filePath && !img.error && !img.isLoading) ||
|
||||||
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
allDroppedFiles.some((file) => !file.error && !file.isLoading));
|
||||||
@@ -1073,7 +1073,7 @@ export default function ChatInput({
|
|||||||
isAnyDroppedFileLoading ||
|
isAnyDroppedFileLoading ||
|
||||||
isRecording ||
|
isRecording ||
|
||||||
isTranscribing ||
|
isTranscribing ||
|
||||||
isLoadingSummary
|
isLoadingCompaction
|
||||||
}
|
}
|
||||||
className={`rounded-full px-10 py-2 flex items-center gap-2 ${
|
className={`rounded-full px-10 py-2 flex items-center gap-2 ${
|
||||||
!hasSubmittableContent ||
|
!hasSubmittableContent ||
|
||||||
@@ -1081,12 +1081,12 @@ export default function ChatInput({
|
|||||||
isAnyDroppedFileLoading ||
|
isAnyDroppedFileLoading ||
|
||||||
isRecording ||
|
isRecording ||
|
||||||
isTranscribing ||
|
isTranscribing ||
|
||||||
isLoadingSummary
|
isLoadingCompaction
|
||||||
? 'bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600'
|
? 'bg-slate-600 text-white cursor-not-allowed opacity-50 border-slate-600'
|
||||||
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600 hover:cursor-pointer'
|
: 'bg-slate-600 text-white hover:bg-slate-700 border-slate-600 hover:cursor-pointer'
|
||||||
}`}
|
}`}
|
||||||
title={
|
title={
|
||||||
isLoadingSummary
|
isLoadingCompaction
|
||||||
? 'Summarizing conversation...'
|
? 'Summarizing conversation...'
|
||||||
: isAnyImageLoading
|
: isAnyImageLoading
|
||||||
? 'Waiting for images to save...'
|
? 'Waiting for images to save...'
|
||||||
@@ -1287,7 +1287,7 @@ export default function ChatInput({
|
|||||||
<div className="w-px h-4 bg-border-default mx-2" />
|
<div className="w-px h-4 bg-border-default mx-2" />
|
||||||
<BottomMenuModeSelection />
|
<BottomMenuModeSelection />
|
||||||
{messages.length > 0 && (
|
{messages.length > 0 && (
|
||||||
<ManualSummarizeButton
|
<ManualCompactButton
|
||||||
messages={messages}
|
messages={messages}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
setMessages={setMessages}
|
setMessages={setMessages}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ interface ChatContextManagerState {
|
|||||||
summaryContent: string;
|
summaryContent: string;
|
||||||
summarizedThread: Message[];
|
summarizedThread: Message[];
|
||||||
isSummaryModalOpen: boolean;
|
isSummaryModalOpen: boolean;
|
||||||
isLoadingSummary: boolean;
|
isLoadingCompaction: boolean;
|
||||||
errorLoadingSummary: boolean;
|
errorLoadingSummary: boolean;
|
||||||
preparingManualSummary: boolean;
|
preparingManualSummary: boolean;
|
||||||
}
|
}
|
||||||
@@ -32,10 +32,7 @@ interface ChatContextManagerActions {
|
|||||||
hasSummarizationRequestedContent: (message: Message) => boolean;
|
hasSummarizationRequestedContent: (message: Message) => boolean;
|
||||||
getContextHandlerType: (message: Message) => 'contextLengthExceeded' | 'summarizationRequested';
|
getContextHandlerType: (message: Message) => 'contextLengthExceeded' | 'summarizationRequested';
|
||||||
handleContextLengthExceeded: (messages: Message[]) => Promise<void>;
|
handleContextLengthExceeded: (messages: Message[]) => Promise<void>;
|
||||||
handleManualSummarization: (
|
handleManualCompaction: (messages: Message[], setMessages: (messages: Message[]) => void) => void;
|
||||||
messages: Message[],
|
|
||||||
setMessages: (messages: Message[]) => void
|
|
||||||
) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the context
|
// Create the context
|
||||||
@@ -50,12 +47,12 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
const [summaryContent, setSummaryContent] = useState<string>('');
|
const [summaryContent, setSummaryContent] = useState<string>('');
|
||||||
const [summarizedThread, setSummarizedThread] = useState<Message[]>([]);
|
const [summarizedThread, setSummarizedThread] = useState<Message[]>([]);
|
||||||
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState<boolean>(false);
|
const [isSummaryModalOpen, setIsSummaryModalOpen] = useState<boolean>(false);
|
||||||
const [isLoadingSummary, setIsLoadingSummary] = useState<boolean>(false);
|
const [isLoadingCompaction, setIsLoadingCompaction] = useState<boolean>(false);
|
||||||
const [errorLoadingSummary, setErrorLoadingSummary] = useState<boolean>(false);
|
const [errorLoadingSummary, setErrorLoadingSummary] = useState<boolean>(false);
|
||||||
const [preparingManualSummary, setPreparingManualSummary] = useState<boolean>(false);
|
const [preparingManualSummary, setPreparingManualSummary] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
||||||
setIsLoadingSummary(true);
|
setIsLoadingCompaction(true);
|
||||||
setErrorLoadingSummary(false);
|
setErrorLoadingSummary(false);
|
||||||
setPreparingManualSummary(true);
|
setPreparingManualSummary(true);
|
||||||
|
|
||||||
@@ -79,17 +76,17 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
setSummarizedThread(convertedMessages);
|
setSummarizedThread(convertedMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsLoadingSummary(false);
|
setIsLoadingCompaction(false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error handling context length exceeded:', err);
|
console.error('Error handling context length exceeded:', err);
|
||||||
setErrorLoadingSummary(true);
|
setErrorLoadingSummary(true);
|
||||||
setIsLoadingSummary(false);
|
setIsLoadingCompaction(false);
|
||||||
} finally {
|
} finally {
|
||||||
setPreparingManualSummary(false);
|
setPreparingManualSummary(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleManualSummarization = (
|
const handleManualCompaction = (
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
setMessages: (messages: Message[]) => void
|
setMessages: (messages: Message[]) => void
|
||||||
): void => {
|
): void => {
|
||||||
@@ -242,7 +239,7 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
summaryContent,
|
summaryContent,
|
||||||
summarizedThread,
|
summarizedThread,
|
||||||
isSummaryModalOpen,
|
isSummaryModalOpen,
|
||||||
isLoadingSummary,
|
isLoadingCompaction,
|
||||||
errorLoadingSummary,
|
errorLoadingSummary,
|
||||||
preparingManualSummary,
|
preparingManualSummary,
|
||||||
|
|
||||||
@@ -256,7 +253,7 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
hasSummarizationRequestedContent,
|
hasSummarizationRequestedContent,
|
||||||
getContextHandlerType,
|
getContextHandlerType,
|
||||||
handleContextLengthExceeded,
|
handleContextLengthExceeded,
|
||||||
handleManualSummarization,
|
handleManualCompaction,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const ContextHandler: React.FC<ContextHandlerProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
summaryContent,
|
summaryContent,
|
||||||
isLoadingSummary,
|
isLoadingCompaction,
|
||||||
errorLoadingSummary,
|
errorLoadingSummary,
|
||||||
openSummaryModal,
|
openSummaryModal,
|
||||||
handleContextLengthExceeded,
|
handleContextLengthExceeded,
|
||||||
@@ -62,13 +62,13 @@ export const ContextHandler: React.FC<ContextHandlerProps> = ({
|
|||||||
|
|
||||||
// Scroll when summarization starts (loading state)
|
// Scroll when summarization starts (loading state)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoadingSummary && shouldAllowSummaryInteraction) {
|
if (isLoadingCompaction && shouldAllowSummaryInteraction) {
|
||||||
// Delay the scroll slightly to ensure the loading content is rendered
|
// Delay the scroll slightly to ensure the loading content is rendered
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
onSummaryComplete?.();
|
onSummaryComplete?.();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
}, [isLoadingSummary, shouldAllowSummaryInteraction, onSummaryComplete]);
|
}, [isLoadingCompaction, shouldAllowSummaryInteraction, onSummaryComplete]);
|
||||||
|
|
||||||
// Function to trigger the async operation properly
|
// Function to trigger the async operation properly
|
||||||
const triggerContextLengthExceeded = () => {
|
const triggerContextLengthExceeded = () => {
|
||||||
@@ -234,7 +234,7 @@ export const ContextHandler: React.FC<ContextHandlerProps> = ({
|
|||||||
<div className="flex-grow border-t border-gray-300"></div>
|
<div className="flex-grow border-t border-gray-300"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{isLoadingSummary && shouldAllowSummaryInteraction
|
{isLoadingCompaction && shouldAllowSummaryInteraction
|
||||||
? renderLoadingState()
|
? renderLoadingState()
|
||||||
: renderContentState()}
|
: renderContentState()}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+14
-13
@@ -14,18 +14,18 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/Tooltip';
|
|||||||
import { useChatContextManager } from './ChatContextManager';
|
import { useChatContextManager } from './ChatContextManager';
|
||||||
import { Message } from '../../types/message';
|
import { Message } from '../../types/message';
|
||||||
|
|
||||||
interface ManualSummarizeButtonProps {
|
interface ManualCompactButtonProps {
|
||||||
messages: Message[];
|
messages: Message[];
|
||||||
isLoading?: boolean; // need this prop to know if Goose is responding
|
isLoading?: boolean; // need this prop to know if Goose is responding
|
||||||
setMessages: (messages: Message[]) => void; // context management is triggered via special message content types
|
setMessages: (messages: Message[]) => void; // context management is triggered via special message content types
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
export const ManualCompactButton: React.FC<ManualCompactButtonProps> = ({
|
||||||
messages,
|
messages,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
setMessages,
|
setMessages,
|
||||||
}) => {
|
}) => {
|
||||||
const { handleManualSummarization, isLoadingSummary } = useChatContextManager();
|
const { handleManualCompaction, isLoadingCompaction } = useChatContextManager();
|
||||||
|
|
||||||
const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
|
const [isConfirmationOpen, setIsConfirmationOpen] = useState(false);
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
|||||||
setIsConfirmationOpen(true);
|
setIsConfirmationOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSummarize = async () => {
|
const handleCompaction = async () => {
|
||||||
setIsConfirmationOpen(false);
|
setIsConfirmationOpen(false);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
handleManualSummarization(messages, setMessages);
|
handleManualCompaction(messages, setMessages);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in handleSummarize:', error);
|
console.error('Error in handleCompaction:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,17 +57,17 @@ export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
|||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center justify-center text-text-default/70 hover:text-text-default text-xs cursor-pointer transition-colors',
|
'flex items-center justify-center text-text-default/70 hover:text-text-default text-xs cursor-pointer transition-colors',
|
||||||
(isLoadingSummary || isLoading) &&
|
(isLoadingCompaction || isLoading) &&
|
||||||
'cursor-not-allowed text-text-default/30 hover:text-text-default/30 opacity-50'
|
'cursor-not-allowed text-text-default/30 hover:text-text-default/30 opacity-50'
|
||||||
)}
|
)}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={isLoadingSummary || isLoading}
|
disabled={isLoadingCompaction || isLoading}
|
||||||
>
|
>
|
||||||
<ScrollText size={16} />
|
<ScrollText size={16} />
|
||||||
</button>
|
</button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
{isLoadingSummary ? 'Summarizing conversation...' : 'Summarize conversation context'}
|
{isLoadingCompaction ? 'Compacting conversation...' : 'Compact conversation context'}
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
@@ -78,10 +78,11 @@ export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
|||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
<ScrollText className="text-iconStandard" size={24} />
|
<ScrollText className="text-iconStandard" size={24} />
|
||||||
Summarize Conversation
|
Compact Conversation
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
This will summarize your conversation history to save context space.
|
This will compact your conversation by summarizing the context into a single message
|
||||||
|
and will help you save context space for future interactions.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
@@ -97,8 +98,8 @@ export const ManualSummarizeButton: React.FC<ManualSummarizeButtonProps> = ({
|
|||||||
<Button type="button" variant="outline" onClick={handleClose}>
|
<Button type="button" variant="outline" onClick={handleClose}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="button" onClick={handleSummarize}>
|
<Button type="button" onClick={handleCompaction}>
|
||||||
Summarize
|
Compact Conversation
|
||||||
</Button>
|
</Button>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
Reference in New Issue
Block a user