Max chat turns settings (default to 5) (#152)

This commit is contained in:
Will Chen
2025-05-13 11:51:44 -07:00
committed by GitHub
parent d1a6b06fd5
commit 14d9a12464
6 changed files with 142 additions and 3 deletions

View File

@@ -0,0 +1,92 @@
import React from "react";
import { useSettings } from "@/hooks/useSettings";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { MAX_CHAT_TURNS_IN_CONTEXT } from "@/constants/settings_constants";
interface OptionInfo {
value: string;
label: string;
description: string;
}
const defaultValue = "default";
const options: OptionInfo[] = [
{
value: "3",
label: "Economy (3)",
description:
"Minimal context to reduce token usage and improve response times.",
},
{
value: defaultValue,
label: `Default (${MAX_CHAT_TURNS_IN_CONTEXT}) `,
description: "Balanced context size for most conversations.",
},
{
value: "10",
label: "High (10)",
description:
"Extended context for complex conversations requiring more history.",
},
{
value: "100",
label: "Max (100)",
description: "Maximum context (not recommended due to cost and speed).",
},
];
export const MaxChatTurnsSelector: React.FC = () => {
const { settings, updateSettings } = useSettings();
const handleValueChange = (value: string) => {
if (value === "default") {
updateSettings({ maxChatTurnsInContext: undefined });
} else {
const numValue = parseInt(value, 10);
updateSettings({ maxChatTurnsInContext: numValue });
}
};
// Determine the current value
const currentValue =
settings?.maxChatTurnsInContext?.toString() || defaultValue;
// Find the current option to display its description
const currentOption =
options.find((opt) => opt.value === currentValue) || options[1];
return (
<div className="space-y-1">
<div className="flex items-center gap-4">
<label
htmlFor="max-chat-turns"
className="text-sm font-medium text-gray-700 dark:text-gray-300"
>
Maximum number of chat turns used in context
</label>
<Select value={currentValue} onValueChange={handleValueChange}>
<SelectTrigger className="w-[180px]" id="max-chat-turns">
<SelectValue placeholder="Select turns" />
</SelectTrigger>
<SelectContent>
{options.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="text-sm text-gray-500 dark:text-gray-400">
{currentOption.description}
</div>
</div>
);
};

View File

@@ -0,0 +1 @@
export const MAX_CHAT_TURNS_IN_CONTEXT = 5;

View File

@@ -28,6 +28,7 @@ import * as os from "os";
import * as crypto from "crypto"; import * as crypto from "crypto";
import { readFile, writeFile, unlink } from "fs/promises"; import { readFile, writeFile, unlink } from "fs/promises";
import { getMaxTokens } from "../utils/token_utils"; import { getMaxTokens } from "../utils/token_utils";
import { MAX_CHAT_TURNS_IN_CONTEXT } from "@/constants/settings_constants";
const logger = log.scope("chat_stream_handlers"); const logger = log.scope("chat_stream_handlers");
@@ -241,6 +242,45 @@ export function registerChatStreamHandlers() {
role: message.role as "user" | "assistant" | "system", role: message.role as "user" | "assistant" | "system",
content: message.content, content: message.content,
})); }));
// Limit chat history based on maxChatTurnsInContext setting
// We add 1 because the current prompt counts as a turn.
const maxChatTurns =
(settings.maxChatTurnsInContext || MAX_CHAT_TURNS_IN_CONTEXT) + 1;
// If we need to limit the context, we take only the most recent turns
let limitedMessageHistory = messageHistory;
if (messageHistory.length > maxChatTurns * 2) {
// Each turn is a user + assistant pair
// Calculate how many messages to keep (maxChatTurns * 2)
let recentMessages = messageHistory
.filter((msg) => msg.role !== "system")
.slice(-maxChatTurns * 2);
// Ensure the first message is a user message
if (recentMessages.length > 0 && recentMessages[0].role !== "user") {
// Find the first user message
const firstUserIndex = recentMessages.findIndex(
(msg) => msg.role === "user",
);
if (firstUserIndex > 0) {
// Drop assistant messages before the first user message
recentMessages = recentMessages.slice(firstUserIndex);
} else if (firstUserIndex === -1) {
logger.warn(
"No user messages found in recent history, set recent messages to empty",
);
recentMessages = [];
}
}
limitedMessageHistory = [...recentMessages];
logger.log(
`Limiting chat history from ${messageHistory.length} to ${limitedMessageHistory.length} messages (max ${maxChatTurns} turns)`,
);
}
let systemPrompt = SYSTEM_PROMPT; let systemPrompt = SYSTEM_PROMPT;
if ( if (
updatedChat.app?.supabaseProjectId && updatedChat.app?.supabaseProjectId &&
@@ -292,7 +332,7 @@ This conversation includes one or more image attachments. When the user uploads
role: "assistant", role: "assistant",
content: "OK, got it. I'm ready to help", content: "OK, got it. I'm ready to help",
}, },
...messageHistory.map((msg) => ({ ...limitedMessageHistory.map((msg) => ({
role: msg.role as "user" | "assistant" | "system", role: msg.role as "user" | "assistant" | "system",
content: msg.content, content: msg.content,
})), })),

View File

@@ -285,9 +285,9 @@ const getProposalHandler = async (
); );
// If we're using more than 80% of the context window, suggest summarizing // If we're using more than 80% of the context window, suggest summarizing
if (totalTokens > contextWindow * 0.8) { if (totalTokens > contextWindow * 0.8 || chat.messages.length > 10) {
logger.log( logger.log(
`Token usage high (${totalTokens}/${contextWindow}), suggesting summarize action`, `Token usage is high (${totalTokens}/${contextWindow}) OR long chat history (${chat.messages.length} messages), suggesting summarize action`,
); );
actions.push({ actions.push({
id: "summarize-in-new-chat", id: "summarize-in-new-chat",

View File

@@ -117,6 +117,7 @@ export const UserSettingsSchema = z.object({
dyadProBudget: DyadProBudgetSchema.optional(), dyadProBudget: DyadProBudgetSchema.optional(),
experiments: ExperimentsSchema.optional(), experiments: ExperimentsSchema.optional(),
lastShownReleaseNotesVersion: z.string().optional(), lastShownReleaseNotesVersion: z.string().optional(),
maxChatTurnsInContext: z.number().optional(),
// DEPRECATED. // DEPRECATED.
runtimeMode: RuntimeModeSchema.optional(), runtimeMode: RuntimeModeSchema.optional(),
}); });

View File

@@ -6,6 +6,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import { showSuccess, showError } from "@/lib/toast"; import { showSuccess, showError } from "@/lib/toast";
import { AutoApproveSwitch } from "@/components/AutoApproveSwitch"; import { AutoApproveSwitch } from "@/components/AutoApproveSwitch";
import { TelemetrySwitch } from "@/components/TelemetrySwitch"; import { TelemetrySwitch } from "@/components/TelemetrySwitch";
import { MaxChatTurnsSelector } from "@/components/MaxChatTurnsSelector";
import { useSettings } from "@/hooks/useSettings"; import { useSettings } from "@/hooks/useSettings";
import { useAppVersion } from "@/hooks/useAppVersion"; import { useAppVersion } from "@/hooks/useAppVersion";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@@ -106,6 +107,10 @@ export default function SettingsPage() {
This will automatically approve code changes and run them. This will automatically approve code changes and run them.
</div> </div>
</div> </div>
<div className="mt-4">
<MaxChatTurnsSelector />
</div>
</div> </div>
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm"> <div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm">