accept ai_fallback for new workflows (#3358)

This commit is contained in:
Jonathan Dobson
2025-09-04 12:20:22 -04:00
committed by GitHub
parent f3392899e2
commit 3299a9fcdd
5 changed files with 143 additions and 128 deletions

View File

@@ -48,6 +48,7 @@ import {
MAX_SCREENSHOT_SCROLLS_DEFAULT, MAX_SCREENSHOT_SCROLLS_DEFAULT,
MAX_STEPS_DEFAULT, MAX_STEPS_DEFAULT,
} from "@/routes/workflows/editor/nodes/Taskv2Node/types"; } from "@/routes/workflows/editor/nodes/Taskv2Node/types";
import { OrgWalled } from "@/components/Orgwalled";
function createTemplateTaskFromTaskGenerationParameters( function createTemplateTaskFromTaskGenerationParameters(
values: TaskGenerationApiResponse, values: TaskGenerationApiResponse,
@@ -470,22 +471,23 @@ function PromptBox() {
/> />
</div> </div>
</div> </div>
<OrgWalled className="p-0">
<div className="flex gap-16"> <div className="flex gap-16">
<div className="w-48 shrink-0"> <div className="w-48 shrink-0">
<div className="text-sm">Generate Script</div> <div className="text-sm">Generate Script</div>
<div className="text-xs text-slate-400"> <div className="text-xs text-slate-400">
Whether to generate scripts for this task run (on Whether to generate scripts for this task run (on
success). success).
</div>
</div> </div>
<Switch
checked={generateScript}
onCheckedChange={(checked) => {
setGenerateScript(Boolean(checked));
}}
/>
</div> </div>
<Switch </OrgWalled>
checked={generateScript}
onCheckedChange={(checked) => {
setGenerateScript(Boolean(checked));
}}
/>
</div>
<div className="flex gap-16"> <div className="flex gap-16">
<div className="w-48 shrink-0"> <div className="w-48 shrink-0">
<div className="text-sm">Publish Workflow</div> <div className="text-sm">Publish Workflow</div>

View File

@@ -30,6 +30,7 @@ import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
import { cn } from "@/util/utils"; import { cn } from "@/util/utils";
import { WorkflowApiResponse } from "../types/workflowTypes"; import { WorkflowApiResponse } from "../types/workflowTypes";
import { CacheKeyValuesResponse } from "@/routes/workflows/types/scriptTypes"; import { CacheKeyValuesResponse } from "@/routes/workflows/types/scriptTypes";
import { OrgWalled } from "@/components/Orgwalled";
interface Dom { interface Dom {
input: React.MutableRefObject<HTMLInputElement | null>; input: React.MutableRefObject<HTMLInputElement | null>;
@@ -125,62 +126,64 @@ function WorkflowHeader({
<div className="flex h-full items-center justify-end gap-4"> <div className="flex h-full items-center justify-end gap-4">
{user && workflow.generate_script && ( {user && workflow.generate_script && (
// (cacheKeyValues?.total_count ?? 0) > 0 && ( // (cacheKeyValues?.total_count ?? 0) > 0 && (
<div <OrgWalled className="p-0">
tabIndex={1} <div
className="flex max-w-[15rem] items-center justify-center gap-1 rounded-md border border-input pr-1 focus-within:ring-1 focus-within:ring-ring" tabIndex={1}
> className="flex max-w-[15rem] items-center justify-center gap-1 rounded-md border border-input pr-1 focus-within:ring-1 focus-within:ring-ring"
<Input >
ref={dom.input} <Input
className="focus-visible:transparent focus-visible:none h-[2.75rem] text-ellipsis whitespace-nowrap border-none focus-visible:outline-none focus-visible:ring-0" ref={dom.input}
onChange={(e) => { className="focus-visible:transparent focus-visible:none h-[2.75rem] text-ellipsis whitespace-nowrap border-none focus-visible:outline-none focus-visible:ring-0"
setChosenCacheKeyValue(e.target.value); onChange={(e) => {
onCacheKeyValuesFilter(e.target.value); setChosenCacheKeyValue(e.target.value);
}} onCacheKeyValuesFilter(e.target.value);
onMouseDown={() => { }}
if (!cacheKeyValuesPanelOpen) { onMouseDown={() => {
onCacheKeyValuesClick(); if (!cacheKeyValuesPanelOpen) {
} onCacheKeyValuesClick();
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
const numFiltered = cacheKeyValues?.values?.length ?? 0;
if (numFiltered === 1) {
const first = cacheKeyValues?.values?.[0];
if (first) {
setChosenCacheKeyValue(first);
onCacheKeyValueAccept(first);
}
return;
} }
}}
onKeyDown={(e) => {
if (e.key === "Enter") {
const numFiltered = cacheKeyValues?.values?.length ?? 0;
setChosenCacheKeyValue(chosenCacheKeyValue); if (numFiltered === 1) {
onCacheKeyValueAccept(chosenCacheKeyValue); const first = cacheKeyValues?.values?.[0];
} if (first) {
onCacheKeyValuesKeydown(e); setChosenCacheKeyValue(first);
}} onCacheKeyValueAccept(first);
placeholder="Code Key Value" }
value={chosenCacheKeyValue ?? undefined} return;
onBlur={(e) => { }
onCacheKeyValuesBlurred(e.target.value);
setChosenCacheKeyValue(e.target.value); setChosenCacheKeyValue(chosenCacheKeyValue);
}} onCacheKeyValueAccept(chosenCacheKeyValue);
/> }
{cacheKeyValuesPanelOpen ? ( onCacheKeyValuesKeydown(e);
<ChevronUpIcon }}
className="h-6 w-6 cursor-pointer" placeholder="Code Key Value"
onClick={onCacheKeyValuesClick} value={chosenCacheKeyValue ?? undefined}
/> onBlur={(e) => {
) : ( onCacheKeyValuesBlurred(e.target.value);
<ChevronDownIcon setChosenCacheKeyValue(e.target.value);
className="h-6 w-6 cursor-pointer"
onClick={() => {
dom.input.current?.focus();
onCacheKeyValuesClick();
}} }}
/> />
)} {cacheKeyValuesPanelOpen ? (
</div> <ChevronUpIcon
className="h-6 w-6 cursor-pointer"
onClick={onCacheKeyValuesClick}
/>
) : (
<ChevronDownIcon
className="h-6 w-6 cursor-pointer"
onClick={() => {
dom.input.current?.focus();
onCacheKeyValuesClick();
}}
/>
)}
</div>
</OrgWalled>
)} )}
{isGlobalWorkflow ? ( {isGlobalWorkflow ? (
<Button <Button

View File

@@ -7,6 +7,7 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import { DotsHorizontalIcon } from "@radix-ui/react-icons"; import { DotsHorizontalIcon } from "@radix-ui/react-icons";
import { OrgWalled } from "@/components/Orgwalled";
type Props = { type Props = {
isDeletable?: boolean; isDeletable?: boolean;
@@ -44,14 +45,18 @@ function NodeActionMenu({
Delete Block Delete Block
</DropdownMenuItem> </DropdownMenuItem>
)} )}
{isScriptable && onShowScript && ( {isScriptable && (
<DropdownMenuItem <OrgWalled className="p-0">
onSelect={() => { {onShowScript && (
onShowScript(); <DropdownMenuItem
}} onSelect={() => {
> onShowScript();
{showScriptText ?? "Show Code"} }}
</DropdownMenuItem> >
{showScriptText ?? "Show Code"}
</DropdownMenuItem>
)}
</OrgWalled>
)} )}
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>

View File

@@ -30,6 +30,7 @@ import { ModelSelector } from "@/components/ModelSelector";
import { WorkflowModel } from "@/routes/workflows/types/workflowTypes"; import { WorkflowModel } from "@/routes/workflows/types/workflowTypes";
import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "../Taskv2Node/types"; import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "../Taskv2Node/types";
import { KeyValueInput } from "@/components/KeyValueInput"; import { KeyValueInput } from "@/components/KeyValueInput";
import { OrgWalled } from "@/components/Orgwalled";
import { placeholders } from "@/routes/workflows/editor/helpContent"; import { placeholders } from "@/routes/workflows/editor/helpContent";
import { useToggleScriptForNodeCallback } from "@/routes/workflows/hooks/useToggleScriptForNodeCallback"; import { useToggleScriptForNodeCallback } from "@/routes/workflows/hooks/useToggleScriptForNodeCallback";
import { useWorkflowSettingsStore } from "@/store/WorkflowSettingsStore"; import { useWorkflowSettingsStore } from "@/store/WorkflowSettingsStore";
@@ -162,16 +163,18 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
<div className="relative"> <div className="relative">
<div className="absolute right-[-0.5rem] top-[-0.25rem]"> <div className="absolute right-[-0.5rem] top-[-0.25rem]">
<div> <div>
<Button variant="link" size="icon" onClick={showAllScripts}> <OrgWalled className="p-0">
<TooltipProvider> <Button variant="link" size="icon" onClick={showAllScripts}>
<Tooltip> <TooltipProvider>
<TooltipTrigger asChild> <Tooltip>
<LightningBoltIcon className="h-4 w-4 text-[gold]" /> <TooltipTrigger asChild>
</TooltipTrigger> <LightningBoltIcon className="h-4 w-4 text-[gold]" />
<TooltipContent>Show all code</TooltipContent> </TooltipTrigger>
</Tooltip> <TooltipContent>Show all code</TooltipContent>
</TooltipProvider> </Tooltip>
</Button> </TooltipProvider>
</Button>
</OrgWalled>
</div> </div>
</div> </div>
<header className="mb-6 mt-2">Start</header> <header className="mb-6 mt-2">Start</header>
@@ -224,57 +227,58 @@ function StartNode({ id, data }: NodeProps<StartNode>) {
}} }}
/> />
</div> </div>
<OrgWalled className="p-0 hover:p-0">
<div className="flex flex-col gap-4"> <div className="flex flex-col gap-4">
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label>Generate Code</Label> <Label>Generate Code</Label>
<HelpTooltip content="Generate & use cached code for faster execution." /> <HelpTooltip content="Generate & use cached code for faster execution." />
<Switch <Switch
className="ml-auto" className="ml-auto"
checked={inputs.useScriptCache} checked={inputs.useScriptCache}
onCheckedChange={(value) => { onCheckedChange={(value) => {
handleChange("useScriptCache", value); handleChange("useScriptCache", value);
}}
/>
</div>
</div>
{inputs.useScriptCache && (
<div className="flex flex-col gap-4 rounded-md bg-slate-elevation4 p-4 pl-4">
<div className="space-y-2">
<div className="flex gap-2">
<Label>Code Key (optional)</Label>
<HelpTooltip content="A static or dynamic key for directing code generation." />
</div>
<WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
const v = value.length ? value : null;
handleChange("scriptCacheKey", v);
}} }}
value={inputs.scriptCacheKey ?? ""}
placeholder={
placeholders["scripts"]["scriptKey"]
}
className="nopan text-xs"
/> />
</div> </div>
<div className="space-y-2"> </div>
<div className="flex items-center gap-2"> {inputs.useScriptCache && (
<Label>Fallback To AI On Failure</Label> <div className="flex flex-col gap-4 rounded-md bg-slate-elevation4 p-4 pl-4">
<HelpTooltip content="If cached code fails, fallback to AI." /> <div className="space-y-2">
<Switch <div className="flex gap-2">
className="ml-auto" <Label>Code Key (optional)</Label>
checked={inputs.aiFallback} <HelpTooltip content="A static or dynamic key for directing code generation." />
onCheckedChange={(value) => { </div>
handleChange("aiFallback", value); <WorkflowBlockInputTextarea
nodeId={id}
onChange={(value) => {
const v = value.length ? value : null;
handleChange("scriptCacheKey", v);
}} }}
value={inputs.scriptCacheKey ?? ""}
placeholder={
placeholders["scripts"]["scriptKey"]
}
className="nopan text-xs"
/> />
</div> </div>
<div className="space-y-2">
<div className="flex items-center gap-2">
<Label>Fallback To AI On Failure</Label>
<HelpTooltip content="If cached code fails, fallback to AI." />
<Switch
className="ml-auto"
checked={inputs.aiFallback}
onCheckedChange={(value) => {
handleChange("aiFallback", value);
}}
/>
</div>
</div>
</div> </div>
</div> )}
)} </div>
</div> </OrgWalled>
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label>Save &amp; Reuse Session</Label> <Label>Save &amp; Reuse Session</Label>

View File

@@ -1701,6 +1701,7 @@ class WorkflowService:
status=request.status, status=request.status,
generate_script=request.generate_script, generate_script=request.generate_script,
cache_key=request.cache_key, cache_key=request.cache_key,
ai_fallback=request.ai_fallback,
) )
# Keeping track of the new workflow id to delete it if an error occurs during the creation process # Keeping track of the new workflow id to delete it if an error occurs during the creation process
new_workflow_id = workflow.workflow_id new_workflow_id = workflow.workflow_id