import { randomUUID } from 'node:crypto' import type { WorkflowSpecInput, WorkflowSpecOutput, WorkflowSpecSettings } from './workflow-builder-types.ts' import { stepRef } from './workflow-references.ts' export function createWorkflowInput(params: WorkflowSpecInput): WorkflowSpecInput { return { ...params, id: params.id ?? randomUUID(), } } export function buildSpecInputs(inputs?: WorkflowSpecInput[]): Record { const result: Record = {} for (const input of inputs ?? []) { result[input.name] = { ...(input.id && { id: input.id }), name: input.name, type: input.type, required: input.required ?? true, ...(input.description && { description: input.description }), ...(input.multimodal !== undefined && { processingOptions: { allowMultimodal: input.multimodal }, }), } } return result } /** * Build a workflow `outputs` map from a step's structured output schema. * * Every property becomes a reviewable workflow output sourced from that step. Call it once per * step whose fields the reviewer should see and merge the results. */ export function outputsFromStep( step: { id: string }, schema: Record, ): Record { const outputs: Record = {} for (const [name, property] of Object.entries(schema)) { outputs[name] = { type: property.type ?? 'string', source: stepRef(step, { path: name }), } } return outputs } /** * Build workflow spec for graph-authored workflows. * The graph is the source of truth — steps stay empty. * * `outputs` declares what the workflow returns, and those fields are what becomes reviewable * attribute by attribute on a test case result. Leaving it empty produces a workflow whose test * cases can be executed but never reviewed — build it with {@link outputsFromStep}. */ export function buildWorkflowSpec(params?: { inputs?: WorkflowSpecInput[] outputs?: Record settings?: WorkflowSpecSettings }): Record { return { steps: [], inputs: buildSpecInputs(params?.inputs), outputs: params?.outputs ?? {}, ...(params?.settings && { settings: params.settings }), } } export const buildWorkflowSpecV3 = buildWorkflowSpec