From 84393cffbca830b02367ed58099b13a6dc869c45 Mon Sep 17 00:00:00 2001 From: embire2 Date: Fri, 6 Feb 2026 15:38:44 +0200 Subject: [PATCH] feat: auto prompt optimization --- .../@settings/tabs/features/FeaturesTab.tsx | 21 ++++++++- app/components/chat/Chat.client.tsx | 4 +- app/lib/.server/llm/stream-text.ts | 15 +++++- app/lib/hooks/useSettings.ts | 12 +++++ app/lib/stores/settings.ts | 8 ++++ app/routes/api.chat.ts | 46 ++++++++++++------- 6 files changed, 86 insertions(+), 20 deletions(-) diff --git a/app/components/@settings/tabs/features/FeaturesTab.tsx b/app/components/@settings/tabs/features/FeaturesTab.tsx index 3b14a7565d..e772576486 100644 --- a/app/components/@settings/tabs/features/FeaturesTab.tsx +++ b/app/components/@settings/tabs/features/FeaturesTab.tsx @@ -111,12 +111,14 @@ export default function FeaturesTab() { isLatestBranch, contextOptimizationEnabled, eventLogs, + autoPromptOptimization, setAutoSelectTemplate, enableLatestBranch, enableContextOptimization, setEventLogs, setPromptId, promptId, + setAutoPromptOptimization, } = useSettings(); // Enable features by default on first load @@ -141,6 +143,10 @@ export default function FeaturesTab() { if (eventLogs === undefined) { setEventLogs(true); // Default: ON - Enable event logging } + + if (autoPromptOptimization === undefined) { + setAutoPromptOptimization(true); // Default: ON - Optimize prompts for smaller models + } }, []); // Only run once on component mount const handleToggleFeature = useCallback( @@ -169,12 +175,17 @@ export default function FeaturesTab() { toast.success(`Event logging ${enabled ? 'enabled' : 'disabled'}`); break; } + case 'autoPromptOptimization': { + setAutoPromptOptimization(enabled); + toast.success(`Auto prompt optimization ${enabled ? 'enabled' : 'disabled'}`); + break; + } default: break; } }, - [enableLatestBranch, setAutoSelectTemplate, enableContextOptimization, setEventLogs], + [enableLatestBranch, setAutoSelectTemplate, enableContextOptimization, setEventLogs, setAutoPromptOptimization], ); const features = { @@ -211,6 +222,14 @@ export default function FeaturesTab() { enabled: eventLogs, tooltip: 'Enabled by default to record detailed logs of system events and user actions', }, + { + id: 'autoPromptOptimization', + title: 'Auto Prompt Optimization', + description: 'Use optimized prompts for smaller models automatically', + icon: 'i-ph:magic-wand', + enabled: autoPromptOptimization, + tooltip: 'Enabled by default to keep prompts concise for smaller context windows', + }, ], beta: [], }; diff --git a/app/components/chat/Chat.client.tsx b/app/components/chat/Chat.client.tsx index c4706e1764..e0bf219044 100644 --- a/app/components/chat/Chat.client.tsx +++ b/app/components/chat/Chat.client.tsx @@ -100,7 +100,8 @@ export const ChatImpl = memo( (project) => project.id === supabaseConn.selectedProjectId, ); const supabaseAlert = useStore(workbenchStore.supabaseAlert); - const { activeProviders, promptId, autoSelectTemplate, contextOptimizationEnabled } = useSettings(); + const { activeProviders, promptId, autoSelectTemplate, contextOptimizationEnabled, autoPromptOptimization } = + useSettings(); const [llmErrorAlert, setLlmErrorAlert] = useState(undefined); const [model, setModel] = useState(() => { const savedModel = Cookies.get('selectedModel'); @@ -138,6 +139,7 @@ export const ChatImpl = memo( files, promptId, contextOptimization: contextOptimizationEnabled, + autoPromptOptimization, chatMode, designScheme, supabase: { diff --git a/app/lib/.server/llm/stream-text.ts b/app/lib/.server/llm/stream-text.ts index 40774a8d07..3cab3d8639 100644 --- a/app/lib/.server/llm/stream-text.ts +++ b/app/lib/.server/llm/stream-text.ts @@ -59,6 +59,7 @@ export async function streamText(props: { files?: FileMap; providerSettings?: Record; promptId?: string; + autoPromptOptimization?: boolean; contextOptimization?: boolean; contextFiles?: FileMap; summary?: string; @@ -74,6 +75,7 @@ export async function streamText(props: { files, providerSettings, promptId, + autoPromptOptimization, contextOptimization, contextFiles, summary, @@ -149,8 +151,15 @@ export async function streamText(props: { `Token limits for model ${modelDetails.name}: maxTokens=${safeMaxTokens}, maxTokenAllowed=${modelDetails.maxTokenAllowed}, maxCompletionTokens=${modelDetails.maxCompletionTokens}`, ); + const shouldAutoOptimize = + !!autoPromptOptimization && + (promptId === undefined || promptId === 'default') && + !!modelDetails?.maxTokenAllowed && + modelDetails.maxTokenAllowed <= 16000; + const effectivePromptId = shouldAutoOptimize ? 'optimized' : promptId; + let systemPrompt = - PromptLibrary.getPropmtFromLibrary(promptId || 'default', { + PromptLibrary.getPropmtFromLibrary(effectivePromptId || 'default', { cwd: WORK_DIR, allowedHtmlElements: allowedHTMLElements, modificationTagName: MODIFICATIONS_TAG_NAME, @@ -162,6 +171,10 @@ export async function streamText(props: { }, }) ?? getSystemPrompt(); + if (shouldAutoOptimize) { + logger.info(`Auto prompt optimization enabled: using optimized prompt for ${modelDetails?.name}`); + } + if (chatMode === 'build' && contextFiles && contextOptimization) { const codeContext = createFilesContext(contextFiles, true); diff --git a/app/lib/hooks/useSettings.ts b/app/lib/hooks/useSettings.ts index e5aceb2f9d..5a060bedd5 100644 --- a/app/lib/hooks/useSettings.ts +++ b/app/lib/hooks/useSettings.ts @@ -7,6 +7,7 @@ import { latestBranchStore, autoSelectStarterTemplate, enableContextOptimizationStore, + autoPromptOptimizationStore, tabConfigurationStore, resetTabConfiguration as resetTabConfig, updateProviderSettings as updateProviderSettingsStore, @@ -15,6 +16,7 @@ import { updateContextOptimization, updateEventLogs, updatePromptId, + updateAutoPromptOptimization, } from '~/lib/stores/settings'; import { useCallback, useEffect, useState } from 'react'; import Cookies from 'js-cookie'; @@ -58,6 +60,8 @@ export interface UseSettingsReturn { setAutoSelectTemplate: (enabled: boolean) => void; contextOptimizationEnabled: boolean; enableContextOptimization: (enabled: boolean) => void; + autoPromptOptimization: boolean; + setAutoPromptOptimization: (enabled: boolean) => void; // Tab configuration tabConfiguration: TabWindowConfig; @@ -78,6 +82,7 @@ export function useSettings(): UseSettingsReturn { const autoSelectTemplate = useStore(autoSelectStarterTemplate); const [activeProviders, setActiveProviders] = useState([]); const contextOptimizationEnabled = useStore(enableContextOptimizationStore); + const autoPromptOptimization = useStore(autoPromptOptimizationStore); const tabConfiguration = useStore(tabConfigurationStore); const [settings, setSettings] = useState(() => { const storedSettings = getLocalStorage('settings'); @@ -143,6 +148,11 @@ export function useSettings(): UseSettingsReturn { logStore.logSystem(`Context optimization ${enabled ? 'enabled' : 'disabled'}`); }, []); + const setAutoPromptOptimization = useCallback((enabled: boolean) => { + updateAutoPromptOptimization(enabled); + logStore.logSystem(`Auto prompt optimization ${enabled ? 'enabled' : 'disabled'}`); + }, []); + const setTheme = useCallback( (theme: Settings['theme']) => { saveSettings({ theme }); @@ -197,6 +207,8 @@ export function useSettings(): UseSettingsReturn { setAutoSelectTemplate, contextOptimizationEnabled, enableContextOptimization, + autoPromptOptimization, + setAutoPromptOptimization, setTheme, setLanguage, setNotifications, diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index fb123e6a94..750ebe32af 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -258,6 +258,7 @@ const SETTINGS_KEYS = { EVENT_LOGS: 'isEventLogsEnabled', PROMPT_ID: 'promptId', DEVELOPER_MODE: 'isDeveloperMode', + AUTO_PROMPT_OPTIMIZATION: 'autoPromptOptimization', } as const; // Initialize settings from localStorage or defaults @@ -287,6 +288,7 @@ const getInitialSettings = () => { eventLogs: getStoredBoolean(SETTINGS_KEYS.EVENT_LOGS, true), promptId: isBrowser ? localStorage.getItem(SETTINGS_KEYS.PROMPT_ID) || 'default' : 'default', developerMode: getStoredBoolean(SETTINGS_KEYS.DEVELOPER_MODE, false), + autoPromptOptimization: getStoredBoolean(SETTINGS_KEYS.AUTO_PROMPT_OPTIMIZATION, true), }; }; @@ -298,6 +300,7 @@ export const autoSelectStarterTemplate = atom(initialSettings.autoSelec export const enableContextOptimizationStore = atom(initialSettings.contextOptimization); export const isEventLogsEnabled = atom(initialSettings.eventLogs); export const promptStore = atom(initialSettings.promptId); +export const autoPromptOptimizationStore = atom(initialSettings.autoPromptOptimization); // Helper functions to update settings with persistence export const updateLatestBranch = (enabled: boolean) => { @@ -325,6 +328,11 @@ export const updatePromptId = (id: string) => { localStorage.setItem(SETTINGS_KEYS.PROMPT_ID, id); }; +export const updateAutoPromptOptimization = (enabled: boolean) => { + autoPromptOptimizationStore.set(enabled); + localStorage.setItem(SETTINGS_KEYS.AUTO_PROMPT_OPTIMIZATION, JSON.stringify(enabled)); +}; + // Initialize tab configuration from localStorage or defaults const getInitialTabConfiguration = (): TabWindowConfig => { const defaultConfig: TabWindowConfig = { diff --git a/app/routes/api.chat.ts b/app/routes/api.chat.ts index 73f9176305..d445412db0 100644 --- a/app/routes/api.chat.ts +++ b/app/routes/api.chat.ts @@ -48,24 +48,34 @@ async function chatAction({ context, request }: ActionFunctionArgs) { }, }); - const { messages, files, promptId, contextOptimization, supabase, chatMode, designScheme, maxLLMSteps } = - await request.json<{ - messages: Messages; - files: any; - promptId?: string; - contextOptimization: boolean; - chatMode: 'discuss' | 'build'; - designScheme?: DesignScheme; - supabase?: { - isConnected: boolean; - hasSelectedProject: boolean; - credentials?: { - anonKey?: string; - supabaseUrl?: string; - }; + const { + messages, + files, + promptId, + contextOptimization, + supabase, + chatMode, + designScheme, + maxLLMSteps, + autoPromptOptimization, + } = await request.json<{ + messages: Messages; + files: any; + promptId?: string; + contextOptimization: boolean; + chatMode: 'discuss' | 'build'; + designScheme?: DesignScheme; + autoPromptOptimization?: boolean; + supabase?: { + isConnected: boolean; + hasSelectedProject: boolean; + credentials?: { + anonKey?: string; + supabaseUrl?: string; }; - maxLLMSteps: number; - }>(); + }; + maxLLMSteps: number; + }>(); const cookieHeader = request.headers.get('Cookie'); const apiKeys = JSON.parse(parseCookies(cookieHeader || '').apiKeys || '{}'); @@ -278,6 +288,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) { contextFiles: filteredFiles, chatMode, designScheme, + autoPromptOptimization, summary, messageSliceId, }); @@ -319,6 +330,7 @@ async function chatAction({ context, request }: ActionFunctionArgs) { contextFiles: filteredFiles, chatMode, designScheme, + autoPromptOptimization, summary, messageSliceId, });