redo reverts code (when possible) (#75)

This commit is contained in:
Will Chen
2025-05-02 14:55:46 -07:00
committed by GitHub
parent 5fc49231ee
commit 46cb7ca577
4 changed files with 49 additions and 2 deletions

View File

@@ -53,6 +53,7 @@ import {
TooltipTrigger, TooltipTrigger,
} from "../ui/tooltip"; } from "../ui/tooltip";
import { useNavigate } from "@tanstack/react-router"; import { useNavigate } from "@tanstack/react-router";
import { useVersions } from "@/hooks/useVersions";
const showTokenBarAtom = atom(false); const showTokenBarAtom = atom(false);
@@ -61,6 +62,8 @@ export function ChatInput({ chatId }: { chatId?: number }) {
const [inputValue, setInputValue] = useAtom(chatInputValueAtom); const [inputValue, setInputValue] = useAtom(chatInputValueAtom);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const { settings, updateSettings, isAnyProviderSetup } = useSettings(); const { settings, updateSettings, isAnyProviderSetup } = useSettings();
const appId = useAtomValue(selectedAppIdAtom);
const { refreshVersions } = useVersions(appId);
const { streamMessage, isStreaming, setIsStreaming, error, setError } = const { streamMessage, isStreaming, setIsStreaming, error, setError } =
useStreamChat(); useStreamChat();
const [showError, setShowError] = useState(true); const [showError, setShowError] = useState(true);
@@ -162,6 +165,7 @@ export function ChatInput({ chatId }: { chatId?: number }) {
} finally { } finally {
setIsApproving(false); setIsApproving(false);
setIsPreviewOpen(true); setIsPreviewOpen(true);
refreshVersions();
// Keep same as handleReject // Keep same as handleReject
refreshProposal(); refreshProposal();

View File

@@ -9,6 +9,9 @@ import { selectedChatIdAtom } from "@/atoms/chatAtoms";
import { useAtom, useAtomValue } from "jotai"; import { useAtom, useAtomValue } from "jotai";
import { RefreshCw } from "lucide-react"; import { RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useVersions } from "@/hooks/useVersions";
import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { showError } from "@/lib/toast";
interface MessagesListProps { interface MessagesListProps {
messages: Message[]; messages: Message[];
@@ -17,6 +20,8 @@ interface MessagesListProps {
export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>( export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
function MessagesList({ messages, messagesEndRef }, ref) { function MessagesList({ messages, messagesEndRef }, ref) {
const appId = useAtomValue(selectedAppIdAtom);
const { versions, revertVersion } = useVersions(appId);
const { streamMessage, isStreaming, error, setError } = useStreamChat(); const { streamMessage, isStreaming, error, setError } = useStreamChat();
const { isAnyProviderSetup } = useSettings(); const { isAnyProviderSetup } = useSettings();
const selectedChatId = useAtomValue(selectedChatIdAtom); const selectedChatId = useAtomValue(selectedChatIdAtom);
@@ -39,11 +44,42 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => { onClick={async () => {
if (!selectedChatId) { if (!selectedChatId) {
console.error("No chat selected"); console.error("No chat selected");
return; return;
} }
// The last message is usually an assistant, but it might not be.
const lastVersion = versions[0];
const lastMessage = messages[messages.length - 1];
let reverted = false;
if (
lastVersion.oid === lastMessage.commitHash &&
lastMessage.role === "assistant"
) {
if (versions.length < 2) {
showError("Cannot retry message; no previous version");
return;
}
const previousAssistantMessage =
messages[messages.length - 3];
if (
previousAssistantMessage?.role === "assistant" &&
previousAssistantMessage?.commitHash
) {
console.debug("Reverting to previous assistant version");
await revertVersion({
versionId: previousAssistantMessage.commitHash,
});
reverted = true;
} else {
console.debug("Reverting to previous version");
await revertVersion({
versionId: versions[1].oid,
});
}
}
// Find the last user message // Find the last user message
const lastUserMessage = [...messages] const lastUserMessage = [...messages]
.reverse() .reverse()
@@ -52,10 +88,15 @@ export const MessagesList = forwardRef<HTMLDivElement, MessagesListProps>(
console.error("No user message found"); console.error("No user message found");
return; return;
} }
// If we reverted, we don't need to mark "redo" because
// the old message has already been deleted.
const redo = !reverted;
console.debug("Streaming message with redo", redo);
streamMessage({ streamMessage({
prompt: lastUserMessage.content, prompt: lastUserMessage.content,
chatId: selectedChatId, chatId: selectedChatId,
redo: true, redo,
}); });
}} }}
> >

View File

@@ -494,6 +494,7 @@ export function registerAppHandlers() {
_, _,
{ appId, previousVersionId }: { appId: number; previousVersionId: string } { appId, previousVersionId }: { appId: number; previousVersionId: string }
) => { ) => {
logger.log(`Reverting to version ${previousVersionId} for app ${appId}`);
return withLock(appId, async () => { return withLock(appId, async () => {
const app = await db.query.apps.findFirst({ const app = await db.query.apps.findFirst({
where: eq(apps.id, appId), where: eq(apps.id, appId),

View File

@@ -41,6 +41,7 @@ export interface Message {
role: "user" | "assistant"; role: "user" | "assistant";
content: string; content: string;
approvalState?: "approved" | "rejected" | null; approvalState?: "approved" | "rejected" | null;
commitHash?: string | null;
} }
export interface Chat { export interface Chat {