Skip to content

Commit 55b49a8

Browse files
author
Harrison Ngo
committed
Update workflow sandbox mock then outputs generation
1 parent 5237f40 commit 55b49a8

File tree

4 files changed

+186
-9
lines changed

4 files changed

+186
-9
lines changed

ee/codegen/src/__test__/__snapshots__/workflow-sandbox.test.ts.snap

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`Workflow Sandbox > write > should generate DatasetRow with mocks using array format for then_outputs 1`] = `
4+
"from vellum.workflows import MockNodeExecution
5+
from vellum.workflows.inputs import DatasetRow
6+
from vellum.workflows.sandbox import WorkflowSandboxRunner
7+
8+
from .inputs import Inputs
9+
from .nodes.my_custom_node import MyCustomNode
10+
from .workflow import TestWorkflow
11+
12+
dataset = [
13+
DatasetRow(
14+
label="Scenario with Array Mocks",
15+
inputs=Inputs(test_input="test-value"),
16+
mocks=[
17+
MockNodeExecution(
18+
when_condition=MyCustomNode.Execution.count.greater_than_or_equal_to(0),
19+
then_outputs=MyCustomNode.Outputs(
20+
text_output="This is a mocked text output",
21+
json_output={
22+
"key": "mocked_value",
23+
"nested": {
24+
"data": 123,
25+
},
26+
},
27+
),
28+
),
29+
],
30+
),
31+
]
32+
33+
runner = WorkflowSandboxRunner(workflow=TestWorkflow(), dataset=dataset)
34+
35+
if __name__ == "__main__":
36+
runner.run()
37+
"
38+
`;
39+
340
exports[`Workflow Sandbox > write > should generate DatasetRow with mocks when mocks are provided 1`] = `
441
"from vellum.workflows import MockNodeExecution
542
from vellum.workflows.inputs import DatasetRow

ee/codegen/src/__test__/workflow-sandbox.test.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,5 +430,110 @@ describe("Workflow Sandbox", () => {
430430

431431
expect(result).toMatchSnapshot();
432432
});
433+
434+
it("should generate DatasetRow with mocks using array format for then_outputs", async () => {
435+
const writer = new Writer();
436+
const uniqueWorkflowContext = workflowContextFactory();
437+
const inputVariable: VellumVariable = {
438+
id: "1",
439+
key: "test_input",
440+
type: "STRING",
441+
};
442+
443+
uniqueWorkflowContext.addInputVariableContext(
444+
inputVariableContextFactory({
445+
inputVariableData: inputVariable,
446+
workflowContext: uniqueWorkflowContext,
447+
})
448+
);
449+
450+
// Create a generic node with a known output id
451+
const genericNodeData = genericNodeFactory({
452+
nodeOutputs: [
453+
{
454+
id: "test-output-id-1",
455+
name: "text_output",
456+
type: "STRING",
457+
},
458+
{
459+
id: "test-output-id-2",
460+
name: "json_output",
461+
type: "JSON",
462+
},
463+
],
464+
});
465+
await nodeContextFactory({
466+
workflowContext: uniqueWorkflowContext,
467+
nodeData: genericNodeData,
468+
});
469+
470+
const sandboxInputs: WorkflowSandboxDatasetRow[] = [
471+
{
472+
label: "Scenario with Array Mocks",
473+
inputs: [
474+
{
475+
name: inputVariable.key,
476+
type: "STRING",
477+
value: "test-value",
478+
},
479+
],
480+
mocks: [
481+
{
482+
node_id: genericNodeData.id,
483+
when_condition: {
484+
type: "BINARY_EXPRESSION",
485+
operator: ">=",
486+
lhs: {
487+
type: "EXECUTION_COUNTER",
488+
nodeId: genericNodeData.id,
489+
},
490+
rhs: {
491+
type: "CONSTANT_VALUE",
492+
value: {
493+
type: "NUMBER",
494+
value: 0,
495+
},
496+
},
497+
},
498+
then_outputs: [
499+
{
500+
output_id: "test-output-id-1",
501+
value: {
502+
type: "CONSTANT_VALUE",
503+
value: {
504+
type: "STRING",
505+
value: "This is a mocked text output",
506+
},
507+
},
508+
},
509+
{
510+
output_id: "test-output-id-2",
511+
value: {
512+
type: "CONSTANT_VALUE",
513+
value: {
514+
type: "JSON",
515+
value: { key: "mocked_value", nested: { data: 123 } },
516+
},
517+
},
518+
},
519+
],
520+
},
521+
],
522+
},
523+
];
524+
525+
const sandbox = codegen.workflowSandboxFile({
526+
workflowContext: uniqueWorkflowContext,
527+
sandboxInputs,
528+
});
529+
530+
sandbox.write(writer);
531+
const result = await writer.toStringFormatted();
532+
533+
expect(result).toMatchSnapshot();
534+
expect(result).toContain("text_output=");
535+
expect(result).toContain("json_output=");
536+
expect(result).toContain("This is a mocked text output");
537+
});
433538
});
434539
});

ee/codegen/src/generators/workflow-sandbox-file.ts

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,45 @@ if __name__ == "__main__":
259259
}
260260

261261
// Generate then_outputs by instantiating the node's Outputs class
262-
const outputsArguments: MethodArgument[] = Object.entries(
263-
mock.then_outputs
264-
).map(
265-
([key, value]) =>
266-
new MethodArgument({
267-
name: key,
268-
value: new Json(value),
262+
// Handle both array format (with output_id) and object format (with keys)
263+
let outputsArguments: MethodArgument[];
264+
265+
if (Array.isArray(mock.then_outputs)) {
266+
// Array format: each element has output_id and value
267+
outputsArguments = mock.then_outputs
268+
.map((output) => {
269+
const outputName = nodeContext.getNodeOutputNameById(output.output_id);
270+
if (isNil(outputName)) {
271+
this.workflowContext.addError(
272+
new NodeNotFoundError(
273+
`Failed to find output name for output_id '${output.output_id}' in node '${mock.node_id}'`,
274+
"WARNING"
275+
)
276+
);
277+
return null;
278+
}
279+
return new MethodArgument({
280+
name: outputName,
281+
value: new WorkflowValueDescriptor({
282+
workflowValueDescriptor: output.value,
283+
workflowContext: this.workflowContext,
284+
}),
285+
});
269286
})
270-
);
287+
.filter((arg): arg is MethodArgument => !isNil(arg));
288+
} else if (!isNil(mock.then_outputs)) {
289+
// Object format: keys are output names, values are the output values
290+
outputsArguments = Object.entries(mock.then_outputs).map(
291+
([key, value]) =>
292+
new MethodArgument({
293+
name: key,
294+
value: new Json(value),
295+
})
296+
);
297+
} else {
298+
// No then_outputs provided
299+
outputsArguments = [];
300+
}
271301

272302
const thenOutputsInstance = new ClassInstantiation({
273303
classReference: new Reference({

ee/codegen/src/types/vellum.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -898,10 +898,15 @@ type WorkflowSandboxInput =
898898
| ImageInputRequest
899899
| DocumentInputRequest;
900900
export type WorkflowSandboxInputs = WorkflowSandboxInput[];
901+
export interface WorkflowSandboxDatasetRowMockOutput {
902+
output_id: string;
903+
value: WorkflowValueDescriptor;
904+
}
905+
901906
export interface WorkflowSandboxDatasetRowMock {
902907
node_id: string;
903908
when_condition?: WorkflowValueDescriptor;
904-
then_outputs: Record<string, unknown>;
909+
then_outputs: Record<string, unknown> | WorkflowSandboxDatasetRowMockOutput[];
905910
}
906911

907912
export type WorkflowSandboxDatasetRow =

0 commit comments

Comments
 (0)