Move summarize button inside of context view (#4015)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
import React, { useRef, useState, useEffect, useMemo } from 'react';
|
||||||
import { FolderKey } from 'lucide-react';
|
import { FolderKey, ScrollText } from 'lucide-react';
|
||||||
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/Tooltip';
|
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/Tooltip';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import type { View } from '../App';
|
import type { View } from '../App';
|
||||||
@@ -12,7 +12,6 @@ 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 { 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 +109,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 { isLoadingCompaction } = useChatContextManager();
|
const { isLoadingCompaction, handleManualCompaction } = 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);
|
||||||
@@ -420,7 +419,7 @@ export default function ChatInput({
|
|||||||
autoShow: true, // Auto-show token limit warnings
|
autoShow: true, // Auto-show token limit warnings
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Show info alert only when not in warning/error state
|
// Show info alert with summarize button
|
||||||
addAlert({
|
addAlert({
|
||||||
type: AlertType.Info,
|
type: AlertType.Info,
|
||||||
message: 'Context window',
|
message: 'Context window',
|
||||||
@@ -428,6 +427,11 @@ export default function ChatInput({
|
|||||||
current: numTokens,
|
current: numTokens,
|
||||||
total: tokenLimit,
|
total: tokenLimit,
|
||||||
},
|
},
|
||||||
|
showSummarizeButton: true,
|
||||||
|
onSummarize: () => {
|
||||||
|
handleManualCompaction(messages, setMessages);
|
||||||
|
},
|
||||||
|
summarizeIcon: <ScrollText size={12} />,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (isTokenLimitLoaded && tokenLimit) {
|
} else if (isTokenLimitLoaded && tokenLimit) {
|
||||||
@@ -439,6 +443,14 @@ export default function ChatInput({
|
|||||||
current: 0,
|
current: 0,
|
||||||
total: tokenLimit,
|
total: tokenLimit,
|
||||||
},
|
},
|
||||||
|
showSummarizeButton: messages.length > 0,
|
||||||
|
onSummarize:
|
||||||
|
messages.length > 0
|
||||||
|
? () => {
|
||||||
|
handleManualCompaction(messages, setMessages);
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
summarizeIcon: messages.length > 0 ? <ScrollText size={12} /> : undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1286,13 +1298,6 @@ export default function ChatInput({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<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 && (
|
|
||||||
<ManualCompactButton
|
|
||||||
messages={messages}
|
|
||||||
isLoading={isLoading}
|
|
||||||
setMessages={setMessages}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div className="w-px h-4 bg-border-default mx-2" />
|
<div className="w-px h-4 bg-border-default mx-2" />
|
||||||
<div className="flex items-center h-full">
|
<div className="flex items-center h-full">
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
|
|||||||
@@ -60,6 +60,19 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => {
|
|||||||
: alert.progress!.total}
|
: alert.progress!.total}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{alert.showSummarizeButton && alert.onSummarize && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
alert.onSummarize!();
|
||||||
|
}}
|
||||||
|
className="flex items-center gap-1.5 text-[11px] hover:opacity-80 cursor-pointer outline-none mt-1"
|
||||||
|
>
|
||||||
|
{alert.summarizeIcon}
|
||||||
|
<span>Summarize now</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -16,4 +16,7 @@ export interface Alert {
|
|||||||
current: number;
|
current: number;
|
||||||
total: number;
|
total: number;
|
||||||
};
|
};
|
||||||
|
showSummarizeButton?: boolean;
|
||||||
|
onSummarize?: () => void;
|
||||||
|
summarizeIcon?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import React, { createContext, useContext, useState } from 'react';
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
import { ScrollText } from 'lucide-react';
|
||||||
import { Message } from '../../types/message';
|
import { Message } from '../../types/message';
|
||||||
import {
|
import {
|
||||||
manageContextFromBackend,
|
manageContextFromBackend,
|
||||||
convertApiMessageToFrontendMessage,
|
convertApiMessageToFrontendMessage,
|
||||||
createSummarizationRequestMessage,
|
createSummarizationRequestMessage,
|
||||||
} from './index';
|
} from './index';
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from '../ui/dialog';
|
||||||
|
import { Button } from '../ui/button';
|
||||||
|
|
||||||
// Define the context management interface
|
// Define the context management interface
|
||||||
interface ChatContextManagerState {
|
interface ChatContextManagerState {
|
||||||
@@ -14,6 +24,8 @@ interface ChatContextManagerState {
|
|||||||
isLoadingCompaction: boolean;
|
isLoadingCompaction: boolean;
|
||||||
errorLoadingSummary: boolean;
|
errorLoadingSummary: boolean;
|
||||||
preparingManualSummary: boolean;
|
preparingManualSummary: boolean;
|
||||||
|
isConfirmationOpen: boolean;
|
||||||
|
pendingCompactionData: { messages: Message[]; setMessages: (messages: Message[]) => void } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChatContextManagerActions {
|
interface ChatContextManagerActions {
|
||||||
@@ -50,6 +62,11 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
const [isLoadingCompaction, setIsLoadingCompaction] = 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 [isConfirmationOpen, setIsConfirmationOpen] = useState<boolean>(false);
|
||||||
|
const [pendingCompactionData, setPendingCompactionData] = useState<{
|
||||||
|
messages: Message[];
|
||||||
|
setMessages: (messages: Message[]) => void;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
const handleContextLengthExceeded = async (messages: Message[]): Promise<void> => {
|
||||||
setIsLoadingCompaction(true);
|
setIsLoadingCompaction(true);
|
||||||
@@ -90,6 +107,16 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
messages: Message[],
|
messages: Message[],
|
||||||
setMessages: (messages: Message[]) => void
|
setMessages: (messages: Message[]) => void
|
||||||
): void => {
|
): void => {
|
||||||
|
// Store the pending compaction data and open confirmation dialog
|
||||||
|
setPendingCompactionData({ messages, setMessages });
|
||||||
|
setIsConfirmationOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCompactionConfirm = () => {
|
||||||
|
if (!pendingCompactionData) return;
|
||||||
|
|
||||||
|
const { messages, setMessages } = pendingCompactionData;
|
||||||
|
|
||||||
// add some messages to the message thread
|
// add some messages to the message thread
|
||||||
// these messages will be filtered out in chat view
|
// these messages will be filtered out in chat view
|
||||||
// but they will also be what allows us to render some text in the chatview itself, similar to CLE events
|
// but they will also be what allows us to render some text in the chatview itself, similar to CLE events
|
||||||
@@ -100,6 +127,14 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
|
|
||||||
// add the message to the message thread
|
// add the message to the message thread
|
||||||
setMessages([...messages, summarizationRequest]);
|
setMessages([...messages, summarizationRequest]);
|
||||||
|
|
||||||
|
setIsConfirmationOpen(false);
|
||||||
|
setPendingCompactionData(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCompactionCancel = () => {
|
||||||
|
setIsConfirmationOpen(false);
|
||||||
|
setPendingCompactionData(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSummary = (newSummaryContent: string) => {
|
const updateSummary = (newSummaryContent: string) => {
|
||||||
@@ -242,6 +277,8 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
isLoadingCompaction,
|
isLoadingCompaction,
|
||||||
errorLoadingSummary,
|
errorLoadingSummary,
|
||||||
preparingManualSummary,
|
preparingManualSummary,
|
||||||
|
isConfirmationOpen,
|
||||||
|
pendingCompactionData,
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
updateSummary,
|
updateSummary,
|
||||||
@@ -259,6 +296,39 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
return (
|
return (
|
||||||
<ChatContextManagerContext.Provider value={value}>
|
<ChatContextManagerContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
|
|
||||||
|
{/* Confirmation Modal */}
|
||||||
|
<Dialog open={isConfirmationOpen} onOpenChange={handleCompactionCancel}>
|
||||||
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
<ScrollText className="text-iconStandard" size={24} />
|
||||||
|
Compact Conversation
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogDescription>
|
||||||
|
This will compact your conversation by summarizing the context into a single message
|
||||||
|
and will help you save context space for future interactions.
|
||||||
|
</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="py-4">
|
||||||
|
<p className="text-textStandard">
|
||||||
|
Previous messages will remain visible but only the summary will be included in the
|
||||||
|
active context for Goose. This is useful for long conversations that are approaching
|
||||||
|
the context limit.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter className="pt-2">
|
||||||
|
<Button type="button" variant="outline" onClick={handleCompactionCancel}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button type="button" onClick={handleCompactionConfirm}>
|
||||||
|
Compact Conversation
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
</ChatContextManagerContext.Provider>
|
</ChatContextManagerContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const ManualCompactButton: React.FC<ManualCompactButtonProps> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Confirmation Modal */}
|
{/* Summarization Confirmation Modal */}
|
||||||
<Dialog open={isConfirmationOpen} onOpenChange={handleClose}>
|
<Dialog open={isConfirmationOpen} onOpenChange={handleClose}>
|
||||||
<DialogContent className="sm:max-w-[500px]">
|
<DialogContent className="sm:max-w-[500px]">
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
|
|||||||
Reference in New Issue
Block a user