Jon/sky 6395 refresh code while workflow is running (#3444)

This commit is contained in:
Jonathan Dobson
2025-09-16 14:47:09 -04:00
committed by GitHub
parent ca84e57665
commit d57fccfaaf
4 changed files with 98 additions and 40 deletions

View File

@@ -143,7 +143,7 @@ function Workspace({
? ""
: cacheKeyValueParam
? cacheKeyValueParam
: constructCacheKeyValue(cacheKey, workflow),
: constructCacheKeyValue({ codeKey: cacheKey, workflow }),
);
useEffect(() => {

View File

@@ -1,4 +1,5 @@
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
import { WorkflowRunStatusApiResponse } from "@/api/types";
import {
isDisplayedInWorkflowEditor,
WorkflowEditorParameterTypes,
@@ -113,23 +114,29 @@ const getInitialParameters = (workflow: WorkflowApiResponse) => {
/**
* Attempt to construct a valid code key value from the workflow parameters.
*/
const constructCacheKeyValue = (
codeKey: string,
workflow?: WorkflowApiResponse,
) => {
const constructCacheKeyValue = (opts: {
codeKey: string;
workflow?: WorkflowApiResponse;
workflowRun?: WorkflowRunStatusApiResponse;
}) => {
const { workflow, workflowRun } = opts;
let codeKey = opts.codeKey;
if (!workflow) {
return "";
}
const workflowParameters = getInitialParameters(workflow)
.filter((p) => p.parameterType === "workflow")
.reduce(
(acc, parameter) => {
acc[parameter.key] = parameter.defaultValue;
return acc;
},
{} as Record<string, unknown>,
);
const workflowParameters = workflowRun
? workflowRun?.parameters ?? {}
: getInitialParameters(workflow)
.filter((p) => p.parameterType === "workflow")
.reduce(
(acc, parameter) => {
acc[parameter.key] = parameter.defaultValue;
return acc;
},
{} as Record<string, unknown>,
);
for (const [name, value] of Object.entries(workflowParameters)) {
if (value === null || value === undefined || value === "") {

View File

@@ -7,17 +7,25 @@ type Props = {
cacheKey?: string;
cacheKeyValue?: string;
workflowPermanentId?: string;
pollIntervalMs?: number;
};
function useBlockScriptsQuery({
cacheKey,
cacheKeyValue,
workflowPermanentId,
pollIntervalMs,
}: Props) {
const credentialGetter = useCredentialGetter();
return useQuery<{ [blockName: string]: string }>({
queryKey: ["block-scripts", workflowPermanentId, cacheKey, cacheKeyValue],
queryKey: [
"block-scripts",
workflowPermanentId,
cacheKey,
cacheKeyValue,
pollIntervalMs,
],
queryFn: async () => {
const client = await getClient(credentialGetter, "sans-api-v1");
@@ -30,6 +38,12 @@ function useBlockScriptsQuery({
return result.blocks;
},
refetchInterval: () => {
if (!pollIntervalMs || pollIntervalMs === 0) {
return false;
}
return Math.max(2000, pollIntervalMs);
},
enabled: !!workflowPermanentId,
});
}

View File

@@ -11,10 +11,12 @@ import {
} from "@/components/ui/select";
import { Label } from "@/components/ui/label";
import { HelpTooltip } from "@/components/HelpTooltip";
import { statusIsFinalized } from "@/routes/tasks/types";
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery";
import { useCacheKeyValuesQuery } from "@/routes/workflows/hooks/useCacheKeyValuesQuery";
import { useWorkflowQuery } from "@/routes/workflows/hooks/useWorkflowQuery";
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
import { constructCacheKeyValue } from "@/routes/workflows/editor/utils";
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
@@ -72,12 +74,15 @@ function WorkflowRunCode(props?: Props) {
const showCacheKeyValueSelector = props?.showCacheKeyValueSelector ?? false;
const queryClient = useQueryClient();
const { workflowPermanentId } = useParams();
const { data: workflowRun } = useWorkflowRunQuery();
const { data: workflow } = useWorkflowQuery({
workflowPermanentId,
});
const cacheKey = workflow?.cache_key ?? "";
const [cacheKeyValue, setCacheKeyValue] = useState(
cacheKey === "" ? "" : constructCacheKeyValue(cacheKey, workflow),
cacheKey === ""
? ""
: constructCacheKeyValue({ codeKey: cacheKey, workflow, workflowRun }),
);
const { data: cacheKeyValues } = useCacheKeyValuesQuery({
cacheKey,
@@ -85,13 +90,25 @@ function WorkflowRunCode(props?: Props) {
page: 1,
workflowPermanentId,
});
const isFinalized = workflowRun ? statusIsFinalized(workflowRun) : null;
const parameters = workflowRun?.parameters;
const { data: blockScripts } = useBlockScriptsQuery({
cacheKey,
cacheKeyValue,
workflowPermanentId,
pollIntervalMs: !isFinalized ? 3000 : undefined,
});
const orderedBlockLabels = getOrderedBlockLabels(workflow);
const code = getCode(orderedBlockLabels, blockScripts).join("");
useEffect(() => {
setCacheKeyValue(
cacheKeyValues?.values[0] ?? constructCacheKeyValue(cacheKey, workflow),
constructCacheKeyValue({ codeKey: cacheKey, workflow, workflowRun }) ??
cacheKeyValues?.values[0],
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [cacheKeyValues, setCacheKeyValue, workflow]);
}, [cacheKeyValues, parameters, setCacheKeyValue, workflow, workflowRun]);
useEffect(() => {
queryClient.invalidateQueries({
@@ -104,16 +121,13 @@ function WorkflowRunCode(props?: Props) {
],
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [workflow]);
}, [queryClient, workflow]);
const { data: blockScripts } = useBlockScriptsQuery({
cacheKey,
cacheKeyValue,
workflowPermanentId,
});
const orderedBlockLabels = getOrderedBlockLabels(workflow);
const code = getCode(orderedBlockLabels, blockScripts).join("");
useEffect(() => {
queryClient.invalidateQueries({
queryKey: ["block-scripts", workflowPermanentId, cacheKey, cacheKeyValue],
});
}, [queryClient, workflowRun, workflowPermanentId, cacheKey, cacheKeyValue]);
if (code.length === 0) {
return (
@@ -123,10 +137,7 @@ function WorkflowRunCode(props?: Props) {
);
}
if (
!showCacheKeyValueSelector ||
(cacheKeyValues?.values ?? []).length <= 1
) {
if (!showCacheKeyValueSelector || !cacheKey || cacheKey === "") {
return (
<CodeEditor
className="h-full overflow-y-scroll"
@@ -139,14 +150,33 @@ function WorkflowRunCode(props?: Props) {
);
}
const cacheKeyValueSet = new Set([...(cacheKeyValues?.values ?? [])]);
const cacheKeyValueForWorkflowRun = constructCacheKeyValue({
codeKey: cacheKey,
workflow,
workflowRun,
});
if (cacheKeyValueForWorkflowRun) {
cacheKeyValueSet.add(cacheKeyValueForWorkflowRun);
}
return (
<div className="flex h-full w-full flex-col items-end justify-center gap-2">
<div className="flex w-[20rem] gap-4">
<div className="flex items-center justify-around gap-2">
<Label className="w-[7rem]">Code Cache Key</Label>
<HelpTooltip content="Which generated (& cached) code to view." />
<Label className="w-[7rem]">Code Key Value</Label>
<HelpTooltip
content={
!isFinalized
? "The code key value the generated code is being stored under."
: "Which generated (& cached) code to view."
}
/>
</div>
<Select
disabled={!isFinalized}
value={cacheKeyValue}
onValueChange={(v: string) => setCacheKeyValue(v)}
>
@@ -154,13 +184,20 @@ function WorkflowRunCode(props?: Props) {
<SelectValue placeholder="Code Key Value" />
</SelectTrigger>
<SelectContent>
{(cacheKeyValues?.values ?? []).map((value) => {
return (
<SelectItem key={value} value={value}>
{value}
</SelectItem>
);
})}
{Array.from(cacheKeyValueSet)
.sort()
.map((value) => {
return (
<SelectItem key={value} value={value}>
{value === cacheKeyValueForWorkflowRun &&
isFinalized === true ? (
<span className="underline">{value}</span>
) : (
value
)}
</SelectItem>
);
})}
</SelectContent>
</Select>
</div>