Recipe variables (#5365)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Zane Staggs <zane@squareup.com>
This commit is contained in:
Douwe Osinga
2025-10-28 09:28:47 -04:00
committed by GitHub
parent 1156cbcf9d
commit fb95068e4a
2 changed files with 74 additions and 20 deletions
+26 -19
View File
@@ -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<boolean>();
const [hasNotAcceptedRecipe, setHasNotAcceptedRecipe] = useState<boolean>();
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 && (
<RecipeWarningModal
isOpen={!hasAcceptedRecipe}
isOpen={!!hasNotAcceptedRecipe}
onConfirm={() => handleRecipeAccept(true)}
onCancel={() => handleRecipeAccept(false)}
recipeDetails={{
@@ -306,14 +314,13 @@ function BaseChatContent({
/>
)}
{/*/!* Recipe Parameter Modal *!/*/}
{/*{isParameterModalOpen && filteredParameters.length > 0 && (*/}
{/* <ParameterInputModal*/}
{/* parameters={filteredParameters}*/}
{/* onSubmit={handleParameterSubmit}*/}
{/* onClose={() => setIsParameterModalOpen(false)}*/}
{/* />*/}
{/*)}*/}
{recipe?.parameters && recipe.parameters.length > 0 && !session?.user_recipe_values && (
<ParameterInputModal
parameters={recipe.parameters}
onSubmit={setRecipeUserParams}
onClose={() => setView('chat')}
/>
)}
{/*/!* Create Recipe from Session Modal *!/*/}
{/*<CreateRecipeFromSessionModal*/}
+48 -1
View File
@@ -1,6 +1,13 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { ChatState } from '../types/chatState';
import { Conversation, Message, resumeAgent, Session } from '../api';
import {
Conversation,
Message,
resumeAgent,
Session,
updateFromSession,
updateSessionUserRecipeValues,
} from '../api';
import { getApiUrl } from '../config';
import { createUserMessage, getCompactingMessage, getThinkingMessage } from '../types/message';
@@ -71,6 +78,7 @@ interface UseChatStreamReturn {
messages: Message[];
chatState: ChatState;
handleSubmit: (userMessage: string) => Promise<void>;
setRecipeUserParams: (values: Record<string, string>) => Promise<void>;
stopStreaming: () => void;
sessionLoadError?: string;
}
@@ -370,6 +378,44 @@ export function useChatStream({
[sessionId, setMessagesAndLog, onFinish]
);
const setRecipeUserParams = useCallback(
async (user_recipe_values: Record<string, string>) => {
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,
};
}