diff --git a/src/components/MaxChatTurnsSelector.tsx b/src/components/MaxChatTurnsSelector.tsx
new file mode 100644
index 0000000..d6d0387
--- /dev/null
+++ b/src/components/MaxChatTurnsSelector.tsx
@@ -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 (
+
+
+
+
+
+
+ {currentOption.description}
+
+
+ );
+};
diff --git a/src/constants/settings_constants.ts b/src/constants/settings_constants.ts
new file mode 100644
index 0000000..2bb4455
--- /dev/null
+++ b/src/constants/settings_constants.ts
@@ -0,0 +1 @@
+export const MAX_CHAT_TURNS_IN_CONTEXT = 5;
diff --git a/src/ipc/handlers/chat_stream_handlers.ts b/src/ipc/handlers/chat_stream_handlers.ts
index 9d66953..ae50789 100644
--- a/src/ipc/handlers/chat_stream_handlers.ts
+++ b/src/ipc/handlers/chat_stream_handlers.ts
@@ -28,6 +28,7 @@ import * as os from "os";
import * as crypto from "crypto";
import { readFile, writeFile, unlink } from "fs/promises";
import { getMaxTokens } from "../utils/token_utils";
+import { MAX_CHAT_TURNS_IN_CONTEXT } from "@/constants/settings_constants";
const logger = log.scope("chat_stream_handlers");
@@ -241,6 +242,45 @@ export function registerChatStreamHandlers() {
role: message.role as "user" | "assistant" | "system",
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;
if (
updatedChat.app?.supabaseProjectId &&
@@ -292,7 +332,7 @@ This conversation includes one or more image attachments. When the user uploads
role: "assistant",
content: "OK, got it. I'm ready to help",
},
- ...messageHistory.map((msg) => ({
+ ...limitedMessageHistory.map((msg) => ({
role: msg.role as "user" | "assistant" | "system",
content: msg.content,
})),
diff --git a/src/ipc/handlers/proposal_handlers.ts b/src/ipc/handlers/proposal_handlers.ts
index 9a5bd87..6d94495 100644
--- a/src/ipc/handlers/proposal_handlers.ts
+++ b/src/ipc/handlers/proposal_handlers.ts
@@ -285,9 +285,9 @@ const getProposalHandler = async (
);
// 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(
- `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({
id: "summarize-in-new-chat",
diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts
index 485253f..22c20c3 100644
--- a/src/lib/schemas.ts
+++ b/src/lib/schemas.ts
@@ -117,6 +117,7 @@ export const UserSettingsSchema = z.object({
dyadProBudget: DyadProBudgetSchema.optional(),
experiments: ExperimentsSchema.optional(),
lastShownReleaseNotesVersion: z.string().optional(),
+ maxChatTurnsInContext: z.number().optional(),
// DEPRECATED.
runtimeMode: RuntimeModeSchema.optional(),
});
diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx
index 962ebed..3183c04 100644
--- a/src/pages/settings.tsx
+++ b/src/pages/settings.tsx
@@ -6,6 +6,7 @@ import { IpcClient } from "@/ipc/ipc_client";
import { showSuccess, showError } from "@/lib/toast";
import { AutoApproveSwitch } from "@/components/AutoApproveSwitch";
import { TelemetrySwitch } from "@/components/TelemetrySwitch";
+import { MaxChatTurnsSelector } from "@/components/MaxChatTurnsSelector";
import { useSettings } from "@/hooks/useSettings";
import { useAppVersion } from "@/hooks/useAppVersion";
import { Button } from "@/components/ui/button";
@@ -106,6 +107,10 @@ export default function SettingsPage() {
This will automatically approve code changes and run them.
+
+
+
+