Skip to content
Merged
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
27 changes: 16 additions & 11 deletions ui/desktop/src/components/ChatInput.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
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 { Button } from './ui/button';
import type { View } from '../App';
Expand All @@ -12,7 +12,6 @@ import { Message } from '../types/message';
import { DirSwitcher } from './bottom_menu/DirSwitcher';
import ModelsBottomBar from './settings/models/bottom_bar/ModelsBottomBar';
import { BottomMenuModeSelection } from './bottom_menu/BottomMenuModeSelection';
import { ManualCompactButton } from './context_management/ManualCompactButton';
import { AlertType, useAlerts } from './alerts';
import { useToolCount } from './alerts/useToolCount';
import { useConfig } from './ConfigContext';
Expand Down Expand Up @@ -110,7 +109,7 @@ export default function ChatInput({
const { alerts, addAlert, clearAlerts } = useAlerts();
const dropdownRef = useRef<HTMLDivElement>(null);
const toolCount = useToolCount();
const { isLoadingCompaction } = useChatContextManager();
const { isLoadingCompaction, handleManualCompaction } = useChatContextManager();
const { getProviders, read } = useConfig();
const { getCurrentModelAndProvider, currentModel, currentProvider } = useModelAndProvider();
const [tokenLimit, setTokenLimit] = useState<number>(TOKEN_LIMIT_DEFAULT);
Expand Down Expand Up @@ -420,14 +419,19 @@ export default function ChatInput({
autoShow: true, // Auto-show token limit warnings
});
} else {
// Show info alert only when not in warning/error state
// Show info alert with summarize button
addAlert({
type: AlertType.Info,
message: 'Context window',
progress: {
current: numTokens,
total: tokenLimit,
},
showSummarizeButton: true,
onSummarize: () => {
handleManualCompaction(messages, setMessages);
},
summarizeIcon: <ScrollText size={12} />,
});
}
} else if (isTokenLimitLoaded && tokenLimit) {
Expand All @@ -439,6 +443,14 @@ export default function ChatInput({
current: 0,
total: tokenLimit,
},
showSummarizeButton: messages.length > 0,
onSummarize:
messages.length > 0
? () => {
handleManualCompaction(messages, setMessages);
}
: undefined,
summarizeIcon: messages.length > 0 ? <ScrollText size={12} /> : undefined,
});
}

Expand Down Expand Up @@ -1286,13 +1298,6 @@ export default function ChatInput({
</Tooltip>
<div className="w-px h-4 bg-border-default mx-2" />
<BottomMenuModeSelection />
{messages.length > 0 && (
<ManualCompactButton
messages={messages}
isLoading={isLoading}
setMessages={setMessages}
/>
)}
<div className="w-px h-4 bg-border-default mx-2" />
<div className="flex items-center h-full">
<Tooltip>
Expand Down
13 changes: 13 additions & 0 deletions ui/desktop/src/components/alerts/AlertBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ export const AlertBox = ({ alert, className }: AlertBoxProps) => {
: alert.progress!.total}
</span>
</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>
) : (
<>
Expand Down
3 changes: 3 additions & 0 deletions ui/desktop/src/components/alerts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ export interface Alert {
current: number;
total: number;
};
showSummarizeButton?: boolean;
onSummarize?: () => void;
summarizeIcon?: React.ReactNode;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import React, { createContext, useContext, useState } from 'react';
import { ScrollText } from 'lucide-react';
import { Message } from '../../types/message';
import {
manageContextFromBackend,
convertApiMessageToFrontendMessage,
createSummarizationRequestMessage,
} from './index';
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '../ui/dialog';
import { Button } from '../ui/button';

// Define the context management interface
interface ChatContextManagerState {
Expand All @@ -14,6 +24,8 @@ interface ChatContextManagerState {
isLoadingCompaction: boolean;
errorLoadingSummary: boolean;
preparingManualSummary: boolean;
isConfirmationOpen: boolean;
pendingCompactionData: { messages: Message[]; setMessages: (messages: Message[]) => void } | null;
}

interface ChatContextManagerActions {
Expand Down Expand Up @@ -50,6 +62,11 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
const [isLoadingCompaction, setIsLoadingCompaction] = useState<boolean>(false);
const [errorLoadingSummary, setErrorLoadingSummary] = 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> => {
setIsLoadingCompaction(true);
Expand Down Expand Up @@ -90,6 +107,16 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
messages: Message[],
setMessages: (messages: Message[]) => 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
// 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
Expand All @@ -100,6 +127,14 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>

// add the message to the message thread
setMessages([...messages, summarizationRequest]);

setIsConfirmationOpen(false);
setPendingCompactionData(null);
};

const handleCompactionCancel = () => {
setIsConfirmationOpen(false);
setPendingCompactionData(null);
};

const updateSummary = (newSummaryContent: string) => {
Expand Down Expand Up @@ -242,6 +277,8 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
isLoadingCompaction,
errorLoadingSummary,
preparingManualSummary,
isConfirmationOpen,
pendingCompactionData,

// Actions
updateSummary,
Expand All @@ -259,6 +296,39 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }>
return (
<ChatContextManagerContext.Provider value={value}>
{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>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const ManualCompactButton: React.FC<ManualCompactButtonProps> = ({
</Tooltip>
</div>

{/* Confirmation Modal */}
{/* Summarization Confirmation Modal */}
<Dialog open={isConfirmationOpen} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
Expand Down
Loading