From 85e5ce4ae66df49db762abdfd70c463012af48e1 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Fri, 24 Oct 2025 16:33:38 -0400 Subject: [PATCH 1/2] Recipe variables --- ui/desktop/src/components/BaseChat2.tsx | 45 +++++++++++++---------- ui/desktop/src/hooks/useChatStream.ts | 49 ++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/ui/desktop/src/components/BaseChat2.tsx b/ui/desktop/src/components/BaseChat2.tsx index 0a23906e2d0e..43387cbd691d 100644 --- a/ui/desktop/src/components/BaseChat2.tsx +++ b/ui/desktop/src/components/BaseChat2.tsx @@ -23,6 +23,7 @@ import { useCostTracking } from '../hooks/useCostTracking'; import RecipeActivities from './recipes/RecipeActivities'; import { useToolCount } from './alerts/useToolCount'; import { getThinkingMessage } from '../types/message'; +import ParameterInputModal from './ParameterInputModal'; interface BaseChatProps { setChat: (chat: ChatType) => void; @@ -49,7 +50,7 @@ function BaseChatContent({ const disableAnimation = location.state?.disableAnimation || false; const [hasStartedUsingRecipe, setHasStartedUsingRecipe] = React.useState(false); - const [hasAcceptedRecipe, setHasAcceptedRecipe] = useState(); + const [hasNotAcceptedRecipe, setHasNotAcceptedRecipe] = useState(); const [hasRecipeSecurityWarnings, setHasRecipeSecurityWarnings] = useState(false); const isMobile = useIsMobile(); @@ -63,12 +64,19 @@ function BaseChatContent({ const onStreamFinish = useCallback(() => {}, []); - const { session, messages, chatState, handleSubmit, stopStreaming, sessionLoadError } = - useChatStream({ - sessionId, - onStreamFinish, - initialMessage, - }); + const { + session, + messages, + chatState, + handleSubmit, + stopStreaming, + sessionLoadError, + setRecipeUserParams, + } = useChatStream({ + sessionId, + onStreamFinish, + initialMessage, + }); const handleFormSubmit = (e: React.FormEvent) => { const customEvent = e as unknown as CustomEvent; @@ -95,7 +103,7 @@ function BaseChatContent({ (async () => { const accepted = await window.electron.hasAcceptedRecipeBefore(recipe); - setHasAcceptedRecipe(accepted); + setHasNotAcceptedRecipe(!accepted); if (!accepted) { const scanResult = await scanRecipe(recipe); @@ -107,7 +115,7 @@ function BaseChatContent({ const handleRecipeAccept = async (accept: boolean) => { if (recipe && accept) { await window.electron.recordRecipeHash(recipe); - setHasAcceptedRecipe(true); + setHasNotAcceptedRecipe(false); } else { setView('chat'); } @@ -283,7 +291,7 @@ function BaseChatContent({ sessionCosts={sessionCosts} setIsGoosehintsModalOpen={setIsGoosehintsModalOpen} recipe={recipe} - recipeAccepted={hasAcceptedRecipe} + recipeAccepted={!hasNotAcceptedRecipe} initialPrompt={initialPrompt} toolCount={toolCount || 0} autoSubmit={false} @@ -294,7 +302,7 @@ function BaseChatContent({ {recipe && ( handleRecipeAccept(true)} onCancel={() => handleRecipeAccept(false)} recipeDetails={{ @@ -306,14 +314,13 @@ function BaseChatContent({ /> )} - {/*/!* Recipe Parameter Modal *!/*/} - {/*{isParameterModalOpen && filteredParameters.length > 0 && (*/} - {/* setIsParameterModalOpen(false)}*/} - {/* />*/} - {/*)}*/} + {recipe?.parameters?.length && !session?.user_recipe_values && ( + setView('chat')} + /> + )} {/*/!* Create Recipe from Session Modal *!/*/} {/* Promise; + setRecipeUserParams: (values: Record) => Promise; stopStreaming: () => void; sessionLoadError?: string; } @@ -370,6 +378,44 @@ export function useChatStream({ [sessionId, setMessagesAndLog, onFinish] ); + const setRecipeUserParams = useCallback( + async (user_recipe_values: Record) => { + if (session) { + await updateSessionUserRecipeValues({ + path: { + session_id: sessionId, + }, + body: { + userRecipeValues: user_recipe_values, + }, + throwOnError: true, + }); + // TODO(Douwe): get this from the server instead of emulating it here + setSession({ + ...session, + user_recipe_values, + }); + } else { + setSessionLoadError("can't call setRecipeParams without a session"); + } + }, + [sessionId, session, setSessionLoadError] + ); + + useEffect(() => { + // This should happen on the server when the session is loaded or changed + // use session.id to support changing of sessions rather than depending on the + // stable sessionId. + if (session) { + updateFromSession({ + body: { + session_id: session.id, + }, + throwOnError: true, + }); + } + }, [session]); + useEffect(() => { if (initialMessage && session && messages.length === 0 && chatState === ChatState.Idle) { log.messages('auto-submit-initial', 0, { initialMessage: initialMessage.slice(0, 50) }); @@ -397,5 +443,6 @@ export function useChatStream({ chatState, handleSubmit, stopStreaming, + setRecipeUserParams, }; } From 6d6b671f5c8e1094c846e036e99a92b0e62c17d4 Mon Sep 17 00:00:00 2001 From: Zane Staggs Date: Fri, 24 Oct 2025 14:52:27 -0700 Subject: [PATCH 2/2] fix random 0 showing at the bottom of chat --- ui/desktop/src/components/BaseChat2.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/desktop/src/components/BaseChat2.tsx b/ui/desktop/src/components/BaseChat2.tsx index 43387cbd691d..bef13f7860a7 100644 --- a/ui/desktop/src/components/BaseChat2.tsx +++ b/ui/desktop/src/components/BaseChat2.tsx @@ -314,7 +314,7 @@ function BaseChatContent({ /> )} - {recipe?.parameters?.length && !session?.user_recipe_values && ( + {recipe?.parameters && recipe.parameters.length > 0 && !session?.user_recipe_values && (