Files
windmill/frontend/src/lib/components/ModuleTest.svelte
T
2025-10-17 13:36:05 +00:00

203 lines
5.4 KiB
Svelte

<script lang="ts">
import { ScriptService, type FlowModule, type JavascriptTransform, type Job } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { getScriptByPath } from '$lib/scripts'
import { getContext } from 'svelte'
import type { FlowEditorContext } from './flows/types'
import JobLoader, { type Callbacks } from './JobLoader.svelte'
import { getStepHistoryLoaderContext } from './stepHistoryLoader.svelte'
import { loadSchemaFromModule } from './flows/flowInfers'
interface Props {
mod: FlowModule
testJob?: Job | undefined
testIsLoading?: boolean
noEditor?: boolean
scriptProgress?: any
onJobDone?: () => void
}
let {
mod,
testJob = $bindable(undefined),
testIsLoading = $bindable(false),
noEditor = false,
scriptProgress = $bindable(undefined),
onJobDone
}: Props = $props()
const { flowStore, flowStateStore, pathStore, stepsInputArgs, previewArgs, modulesTestStates } =
getContext<FlowEditorContext>('FlowEditorContext')
let jobLoader: JobLoader | undefined = $state(undefined)
let jobProgressReset: () => void = () => {}
let stepHistoryLoader = getStepHistoryLoaderContext()
export function runTestWithStepArgs() {
const args = stepsInputArgs.getStepArgs(mod.id)
runTest(args)
}
export function loadArgsAndRunTest() {
stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
runTestWithStepArgs()
}
export async function runTest(args: any) {
// Not defined if JobProgressBar not loaded
if (jobProgressReset) jobProgressReset()
if (modulesTestStates.states[mod.id]) {
modulesTestStates.states[mod.id].cancel = async () => {
await jobLoader?.cancelJob()
modulesTestStates.states[mod.id].testJob = undefined
}
modulesTestStates.runTestCb?.(mod.id)
}
const val = mod.value
// let jobId: string | undefined = undefined
let callbacks: Callbacks = {
done: (x) => {
jobDone(x)
}
}
if (val.type == 'rawscript') {
await jobLoader?.runPreview(
val.path ?? ($pathStore ?? '') + '/' + mod.id,
val.content,
val.language,
mod.id === 'preprocessor' ? { _ENTRYPOINT_OVERRIDE: 'preprocessor', ...args } : args,
flowStore?.val?.tag ?? val.tag,
undefined,
undefined,
callbacks
)
} else if (val.type == 'script') {
const script = val.hash
? await ScriptService.getScriptByHash({ workspace: $workspaceStore!, hash: val.hash })
: await getScriptByPath(val.path)
await jobLoader?.runPreview(
val.path,
script.content,
script.language,
mod.id === 'preprocessor' ? { _ENTRYPOINT_OVERRIDE: 'preprocessor', ...args } : args,
flowStore?.val?.tag ?? (val.tag_override ? val.tag_override : script.tag),
script.lock,
val.hash ?? script.hash,
callbacks
)
} else if (val.type == 'flow') {
await jobLoader?.runFlowByPath(val.path, args, callbacks)
} else if (val.type == 'aiagent') {
const { schema } = await loadSchemaFromModule(mod)
const inputTransforms: { [key: string]: JavascriptTransform } = Object.fromEntries(
Object.keys(args).map((key) => [
key,
{
expr: `flow_input.${key}`,
type: 'javascript'
}
])
)
await jobLoader?.runFlowPreview(
args,
{
value: {
modules: [
{
...mod,
value: {
type: 'aiagent',
tools: mod.value.type == 'aiagent' ? mod.value.tools : [],
input_transforms: inputTransforms
}
}
]
},
summary: '',
schema
},
callbacks
)
} else {
throw Error('Not supported module type')
}
}
function jobDone(testJob: Job & { result?: any }) {
if (testJob && !testJob.canceled && testJob.type == 'CompletedJob') {
if (flowStateStore.val[mod.id]) {
flowStateStore.val[mod.id] = {
...flowStateStore.val[mod.id],
previewResult: testJob.result,
previewSuccess: testJob.success,
previewJobId: testJob.id
}
}
stepHistoryLoader?.resetInitial(mod.id)
}
if (modulesTestStates.states[mod.id]) {
modulesTestStates.states[mod.id].testJob = testJob
}
onJobDone?.()
}
export function cancelJob() {
modulesTestStates.states[mod.id]?.cancel?.()
}
$effect(() => {
// Update testIsLoading to read the state from parent components
testIsLoading = modulesTestStates.states?.[mod.id]?.loading ?? false
})
$effect(() => {
// Update testJob to read the state from parent components
testJob = modulesTestStates.states?.[mod.id]?.testJob
})
modulesTestStates.states[mod.id] = {
...(modulesTestStates.states?.[mod.id] ?? { loading: false }),
loading: testIsLoading,
testJob: testJob
}
</script>
<JobLoader
noCode={true}
toastError={noEditor}
bind:scriptProgress
bind:this={jobLoader}
bind:isLoading={
() => modulesTestStates.states[mod.id]?.loading ?? false,
(v) => {
let newLoading = v ?? false
if (modulesTestStates.states && modulesTestStates.states?.[mod.id]?.loading !== newLoading) {
modulesTestStates.states[mod.id] = {
...(modulesTestStates.states?.[mod.id] ?? {}),
loading: newLoading,
hiddenInGraph: false
}
}
}
}
bind:job={
() => modulesTestStates.states[mod.id]?.testJob,
(v) => modulesTestStates.states[mod.id] && (modulesTestStates.states[mod.id].testJob = v)
}
loadPlaceholderJobOnStart={{
type: 'QueuedJob',
id: '',
running: false,
canceled: false,
job_kind: 'preview',
permissioned_as: '',
is_flow_step: false,
email: '',
visible_to_owner: true,
tag: ''
}}
/>