@outputai/core 0.10.1-next.c717e35.0 → 0.10.1-next.cbef793.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/interface/evaluator.d.ts +10 -9
- package/src/interface/evaluator.js +1 -6
- package/src/interface/evaluator.spec.js +10 -10
- package/src/interface/step.d.ts +16 -15
- package/src/interface/step.js +1 -6
- package/src/interface/step.spec.js +10 -10
- package/src/interface/validations/index.js +33 -53
- package/src/interface/validations/index.spec.js +25 -17
- package/src/interface/workflow.d.ts +25 -18
- package/src/interface/workflow.js +6 -17
- package/src/interface/workflow.spec.js +16 -16
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ export type EvaluatorOptions = {
|
|
|
16
16
|
/**
|
|
17
17
|
* The handler function of an evaluator.
|
|
18
18
|
*
|
|
19
|
-
* @param input -
|
|
19
|
+
* @param input - Parsed evaluator input (`z.infer<inputSchema>`).
|
|
20
20
|
*
|
|
21
21
|
* @returns The result of the evaluation.
|
|
22
22
|
*/
|
|
@@ -30,14 +30,15 @@ export type EvaluatorFunction<
|
|
|
30
30
|
/**
|
|
31
31
|
* A wrapper around the user defined `fn` handler function.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* It adds input validation based on the `inputSchema`.
|
|
33
|
+
* Callers pass values accepted by `inputSchema` (`z.input`). The wrapper parses input
|
|
34
|
+
* and invokes `fn`.
|
|
36
35
|
*/
|
|
37
|
-
export type EvaluatorFunctionWrapper<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
export type EvaluatorFunctionWrapper<
|
|
37
|
+
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
38
|
+
Result extends EvaluationResult = EvaluationResult
|
|
39
|
+
> = InputSchema extends AnyZodSchema ?
|
|
40
|
+
( input: z.input<InputSchema> ) => Promise<Result> :
|
|
41
|
+
() => Promise<Result>;
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
44
|
* Creates an evaluation function. It is similar to a step, but must return an EvaluationResult.
|
|
@@ -67,4 +68,4 @@ export declare function evaluator<
|
|
|
67
68
|
inputSchema: InputSchema;
|
|
68
69
|
fn: EvaluatorFunction<InputSchema, Result>;
|
|
69
70
|
options?: EvaluatorOptions;
|
|
70
|
-
} ): EvaluatorFunctionWrapper<
|
|
71
|
+
} ): EvaluatorFunctionWrapper<InputSchema, Result>;
|
|
@@ -13,11 +13,6 @@ export function evaluator( { name, description, inputSchema, fn, options } ) {
|
|
|
13
13
|
description,
|
|
14
14
|
inputSchema,
|
|
15
15
|
options,
|
|
16
|
-
handler: async input =>
|
|
17
|
-
validator.validateInput( input );
|
|
18
|
-
const output = await fn( input );
|
|
19
|
-
validator.validateOutput( output );
|
|
20
|
-
return output;
|
|
21
|
-
}
|
|
16
|
+
handler: async input => validator.parseOutput( await fn( validator.parseInput( input ) ) )
|
|
22
17
|
} );
|
|
23
18
|
}
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
import { ValidationError } from '#errors';
|
|
10
10
|
|
|
11
11
|
const validateDefinitionMock = vi.hoisted( () => vi.fn() );
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const parseInputMock = vi.hoisted( () => vi.fn( input => input ) );
|
|
13
|
+
const parseOutputMock = vi.hoisted( () => vi.fn( output => output ) );
|
|
14
14
|
const validatorConstructorMock = vi.hoisted( () => vi.fn() );
|
|
15
15
|
const createEvaluatorMock = vi.hoisted( () => vi.fn( ( { handler } ) => handler ) );
|
|
16
16
|
|
|
@@ -22,8 +22,8 @@ vi.mock( './validations/index.js', () => {
|
|
|
22
22
|
|
|
23
23
|
constructor( ...args ) {
|
|
24
24
|
validatorConstructorMock( ...args );
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
25
|
+
this.parseInput = parseInputMock;
|
|
26
|
+
this.parseOutput = parseOutputMock;
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -87,15 +87,15 @@ describe( 'evaluator()', () => {
|
|
|
87
87
|
|
|
88
88
|
await expect( wrapper( { value: 'input' } ) ).resolves.toBe( output );
|
|
89
89
|
|
|
90
|
-
expect(
|
|
90
|
+
expect( parseInputMock ).toHaveBeenCalledWith( { value: 'input' } );
|
|
91
91
|
expect( fn ).toHaveBeenCalledWith( { value: 'input' } );
|
|
92
|
-
expect(
|
|
92
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( output );
|
|
93
93
|
} );
|
|
94
94
|
|
|
95
95
|
it( 'does not call the evaluator function when input validation throws', async () => {
|
|
96
96
|
const { evaluator } = await import( './evaluator.js' );
|
|
97
97
|
const error = new ValidationError( 'invalid input' );
|
|
98
|
-
|
|
98
|
+
parseInputMock.mockImplementationOnce( () => {
|
|
99
99
|
throw error;
|
|
100
100
|
} );
|
|
101
101
|
const fn = vi.fn();
|
|
@@ -106,13 +106,13 @@ describe( 'evaluator()', () => {
|
|
|
106
106
|
|
|
107
107
|
await expect( wrapper( { value: 'bad' } ) ).rejects.toBe( error );
|
|
108
108
|
expect( fn ).not.toHaveBeenCalled();
|
|
109
|
-
expect(
|
|
109
|
+
expect( parseOutputMock ).not.toHaveBeenCalled();
|
|
110
110
|
} );
|
|
111
111
|
|
|
112
112
|
it( 'propagates output validation errors after the evaluator function runs', async () => {
|
|
113
113
|
const { evaluator } = await import( './evaluator.js' );
|
|
114
114
|
const error = new ValidationError( 'invalid output' );
|
|
115
|
-
|
|
115
|
+
parseOutputMock.mockImplementationOnce( () => {
|
|
116
116
|
throw error;
|
|
117
117
|
} );
|
|
118
118
|
const output = { value: 'not-an-evaluation-result' };
|
|
@@ -124,7 +124,7 @@ describe( 'evaluator()', () => {
|
|
|
124
124
|
|
|
125
125
|
await expect( wrapper( { value: 'input' } ) ).rejects.toBe( error );
|
|
126
126
|
expect( fn ).toHaveBeenCalledOnce();
|
|
127
|
-
expect(
|
|
127
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( output );
|
|
128
128
|
} );
|
|
129
129
|
} );
|
|
130
130
|
|
package/src/interface/step.d.ts
CHANGED
|
@@ -15,31 +15,32 @@ export type StepOptions = {
|
|
|
15
15
|
/**
|
|
16
16
|
* The handler function of a step.
|
|
17
17
|
*
|
|
18
|
-
* @param input -
|
|
18
|
+
* @param input - Parsed step input (`z.infer<inputSchema>`).
|
|
19
19
|
*
|
|
20
|
-
* @returns A value
|
|
20
|
+
* @returns A value accepted by `outputSchema` before parse (`z.input<outputSchema>`).
|
|
21
21
|
*/
|
|
22
22
|
export type StepFunction<
|
|
23
23
|
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
24
24
|
OutputSchema extends AnyZodSchema | undefined = undefined
|
|
25
25
|
> = InputSchema extends AnyZodSchema ?
|
|
26
|
-
( input: z.infer<InputSchema> ) => Promise<OutputSchema extends AnyZodSchema ? z.
|
|
27
|
-
() => Promise<OutputSchema extends AnyZodSchema ? z.
|
|
26
|
+
( input: z.infer<InputSchema> ) => Promise<OutputSchema extends AnyZodSchema ? z.input<OutputSchema> : void> :
|
|
27
|
+
() => Promise<OutputSchema extends AnyZodSchema ? z.input<OutputSchema> : void>;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* A wrapper around the user defined `fn` handler function.
|
|
31
31
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* It adds input and output validation based on the `inputSchema`, `outputSchema`.
|
|
32
|
+
* Callers pass values accepted by `inputSchema` (`z.input`). The wrapper parses input,
|
|
33
|
+
* invokes `fn`, parses output, and returns `z.infer<outputSchema>`.
|
|
35
34
|
*
|
|
36
|
-
* @param input - The
|
|
37
|
-
* @returns
|
|
35
|
+
* @param input - The step input before `inputSchema` parse.
|
|
36
|
+
* @returns The step output after `outputSchema` parse.
|
|
38
37
|
*/
|
|
39
|
-
export type StepFunctionWrapper<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
export type StepFunctionWrapper<
|
|
39
|
+
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
40
|
+
OutputSchema extends AnyZodSchema | undefined = undefined
|
|
41
|
+
> = InputSchema extends AnyZodSchema ?
|
|
42
|
+
( input: z.input<InputSchema> ) => Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void> :
|
|
43
|
+
() => Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void>;
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Creates a step.
|
|
@@ -123,7 +124,7 @@ export type StepFunctionWrapper<StepFunction extends ( ...args: any ) => any> =
|
|
|
123
124
|
* @param params.outputSchema - Zod schema for the `fn` output
|
|
124
125
|
* @param params.fn - A handler function containing the step code
|
|
125
126
|
* @param params.options - Optional step options.
|
|
126
|
-
* @returns
|
|
127
|
+
* @returns A wrapper that parses input/output around `fn`
|
|
127
128
|
*/
|
|
128
129
|
export declare function step<
|
|
129
130
|
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
@@ -135,4 +136,4 @@ export declare function step<
|
|
|
135
136
|
outputSchema?: OutputSchema;
|
|
136
137
|
fn: StepFunction<InputSchema, OutputSchema>;
|
|
137
138
|
options?: StepOptions;
|
|
138
|
-
} ): StepFunctionWrapper<
|
|
139
|
+
} ): StepFunctionWrapper<InputSchema, OutputSchema>;
|
package/src/interface/step.js
CHANGED
|
@@ -14,11 +14,6 @@ export function step( { name, description, inputSchema, outputSchema, fn, option
|
|
|
14
14
|
inputSchema,
|
|
15
15
|
outputSchema,
|
|
16
16
|
options,
|
|
17
|
-
handler: async input =>
|
|
18
|
-
validator.validateInput( input );
|
|
19
|
-
const output = await fn( input );
|
|
20
|
-
validator.validateOutput( output );
|
|
21
|
-
return output;
|
|
22
|
-
}
|
|
17
|
+
handler: async input => validator.parseOutput( await fn( validator.parseInput( input ) ) )
|
|
23
18
|
} );
|
|
24
19
|
}
|
|
@@ -2,8 +2,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
2
2
|
import { ValidationError } from '#errors';
|
|
3
3
|
|
|
4
4
|
const validateDefinitionMock = vi.hoisted( () => vi.fn() );
|
|
5
|
-
const
|
|
6
|
-
const
|
|
5
|
+
const parseInputMock = vi.hoisted( () => vi.fn( input => input ) );
|
|
6
|
+
const parseOutputMock = vi.hoisted( () => vi.fn( output => output ) );
|
|
7
7
|
const validatorConstructorMock = vi.hoisted( () => vi.fn() );
|
|
8
8
|
const createStepMock = vi.hoisted( () => vi.fn( ( { handler } ) => handler ) );
|
|
9
9
|
|
|
@@ -15,8 +15,8 @@ vi.mock( './validations/index.js', () => {
|
|
|
15
15
|
|
|
16
16
|
constructor( ...args ) {
|
|
17
17
|
validatorConstructorMock( ...args );
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
18
|
+
this.parseInput = parseInputMock;
|
|
19
|
+
this.parseOutput = parseOutputMock;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -86,15 +86,15 @@ describe( 'step()', () => {
|
|
|
86
86
|
|
|
87
87
|
await expect( wrapper( { value: 'input' } ) ).resolves.toBe( output );
|
|
88
88
|
|
|
89
|
-
expect(
|
|
89
|
+
expect( parseInputMock ).toHaveBeenCalledWith( { value: 'input' } );
|
|
90
90
|
expect( fn ).toHaveBeenCalledWith( { value: 'input' } );
|
|
91
|
-
expect(
|
|
91
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( output );
|
|
92
92
|
} );
|
|
93
93
|
|
|
94
94
|
it( 'does not call the step function when input validation throws', async () => {
|
|
95
95
|
const { step } = await import( './step.js' );
|
|
96
96
|
const error = new ValidationError( 'invalid input' );
|
|
97
|
-
|
|
97
|
+
parseInputMock.mockImplementationOnce( () => {
|
|
98
98
|
throw error;
|
|
99
99
|
} );
|
|
100
100
|
const fn = vi.fn();
|
|
@@ -105,13 +105,13 @@ describe( 'step()', () => {
|
|
|
105
105
|
|
|
106
106
|
await expect( wrapper( { value: 'bad' } ) ).rejects.toBe( error );
|
|
107
107
|
expect( fn ).not.toHaveBeenCalled();
|
|
108
|
-
expect(
|
|
108
|
+
expect( parseOutputMock ).not.toHaveBeenCalled();
|
|
109
109
|
} );
|
|
110
110
|
|
|
111
111
|
it( 'propagates output validation errors after the step function runs', async () => {
|
|
112
112
|
const { step } = await import( './step.js' );
|
|
113
113
|
const error = new ValidationError( 'invalid output' );
|
|
114
|
-
|
|
114
|
+
parseOutputMock.mockImplementationOnce( () => {
|
|
115
115
|
throw error;
|
|
116
116
|
} );
|
|
117
117
|
const output = { ok: false };
|
|
@@ -123,6 +123,6 @@ describe( 'step()', () => {
|
|
|
123
123
|
|
|
124
124
|
await expect( wrapper( { value: 'input' } ) ).rejects.toBe( error );
|
|
125
125
|
expect( fn ).toHaveBeenCalledOnce();
|
|
126
|
-
expect(
|
|
126
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( output );
|
|
127
127
|
} );
|
|
128
128
|
} );
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ValidationError } from '#errors';
|
|
2
|
+
import { ComponentType } from '#consts';
|
|
2
3
|
import { prettifyError } from 'zod';
|
|
3
4
|
import {
|
|
4
5
|
evaluatorOutputSchema,
|
|
@@ -10,84 +11,63 @@ import {
|
|
|
10
11
|
workflowSchema
|
|
11
12
|
} from './schemas.js';
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* @param {ZodType} schema
|
|
17
|
-
* @param {string} prefix - Validation error prefix
|
|
18
|
-
* @throws {ValidationError} If validation fails
|
|
19
|
-
* @returns {void}
|
|
20
|
-
*/
|
|
21
|
-
const validate = ( data, schema, prefix = '' ) => {
|
|
14
|
+
const capitalize = word => word.at( 0 ).toUpperCase() + word.slice( 1 );
|
|
15
|
+
|
|
16
|
+
const validate = ( schema, data, prefix ) => {
|
|
22
17
|
if ( !schema ) {
|
|
23
|
-
return;
|
|
18
|
+
return data;
|
|
24
19
|
}
|
|
25
|
-
|
|
26
20
|
const result = schema.safeParse( data );
|
|
27
|
-
if (
|
|
21
|
+
if ( result.success === false ) {
|
|
28
22
|
throw new ValidationError( `${prefix} validation failed: ${prettifyError( result.error ) }` );
|
|
29
23
|
}
|
|
24
|
+
return result.data;
|
|
30
25
|
};
|
|
31
26
|
|
|
32
|
-
export class
|
|
27
|
+
export class Validator {
|
|
28
|
+
static label;
|
|
29
|
+
static definitionSchema;
|
|
30
|
+
|
|
33
31
|
static validateDefinition( definition ) {
|
|
34
|
-
validate(
|
|
32
|
+
validate( this.definitionSchema, definition, this.label );
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
constructor( { name, inputSchema, outputSchema } ) {
|
|
38
36
|
this.name = name;
|
|
39
37
|
this.inputSchema = inputSchema;
|
|
40
38
|
this.outputSchema = outputSchema;
|
|
39
|
+
this.prefix = `${this.constructor.label} "${this.name}"`;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
validate(
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
validateOutput( output ) {
|
|
48
|
-
validate( output, this.outputSchema, `Workflow "${this.name}" output` );
|
|
42
|
+
parseInput( input ) {
|
|
43
|
+
return validate( this.inputSchema, input, `${this.prefix} input` );
|
|
49
44
|
}
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
validate(
|
|
46
|
+
parseOutput( output ) {
|
|
47
|
+
return validate( this.outputSchema, output, `${this.prefix} output` );
|
|
53
48
|
}
|
|
54
49
|
}
|
|
55
50
|
|
|
56
|
-
export class
|
|
57
|
-
static
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
constructor( { name, inputSchema, outputSchema } ) {
|
|
62
|
-
this.name = name;
|
|
63
|
-
this.inputSchema = inputSchema;
|
|
64
|
-
this.outputSchema = outputSchema;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
validateInput( input ) {
|
|
68
|
-
validate( input, this.inputSchema, `Step "${this.name}" input` );
|
|
69
|
-
}
|
|
51
|
+
export class WorkflowValidator extends Validator {
|
|
52
|
+
static label = capitalize( ComponentType.WORKFLOW );
|
|
53
|
+
static definitionSchema = workflowSchema;
|
|
70
54
|
|
|
71
|
-
|
|
72
|
-
validate(
|
|
55
|
+
parseInvocationOptions( options ) {
|
|
56
|
+
return validate( workflowInvocationOptionsSchema, options, `${this.prefix} invocation options` );
|
|
73
57
|
}
|
|
74
58
|
}
|
|
75
59
|
|
|
76
|
-
export class
|
|
77
|
-
static
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
constructor( { name, inputSchema } ) {
|
|
81
|
-
this.name = name;
|
|
82
|
-
this.inputSchema = inputSchema;
|
|
83
|
-
}
|
|
60
|
+
export class StepValidator extends Validator {
|
|
61
|
+
static label = capitalize( ComponentType.STEP );
|
|
62
|
+
static definitionSchema = stepSchema;
|
|
63
|
+
}
|
|
84
64
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
65
|
+
export class EvaluatorValidator extends Validator {
|
|
66
|
+
static label = capitalize( ComponentType.EVALUATOR );
|
|
67
|
+
static definitionSchema = evaluatorSchema;
|
|
88
68
|
|
|
89
|
-
|
|
90
|
-
|
|
69
|
+
constructor( { name, inputSchema } ) {
|
|
70
|
+
super( { name, inputSchema, outputSchema: evaluatorOutputSchema } );
|
|
91
71
|
}
|
|
92
72
|
}
|
|
93
73
|
|
|
@@ -96,7 +76,7 @@ export class EvaluatorValidator {
|
|
|
96
76
|
* @param {object} args - The request arguments
|
|
97
77
|
*/
|
|
98
78
|
export function validateRequestPayload( args ) {
|
|
99
|
-
validate(
|
|
79
|
+
validate( httpRequestSchema, args, 'Request payload' );
|
|
100
80
|
};
|
|
101
81
|
|
|
102
82
|
/**
|
|
@@ -104,5 +84,5 @@ export function validateRequestPayload( args ) {
|
|
|
104
84
|
* @param {object} args - The request arguments
|
|
105
85
|
*/
|
|
106
86
|
export function validateExecuteInParallel( args ) {
|
|
107
|
-
validate(
|
|
87
|
+
validate( executeInParallelSchema, args, 'ExecuteInParallel' );
|
|
108
88
|
};
|
|
@@ -53,10 +53,12 @@ describe( 'interface validators', () => {
|
|
|
53
53
|
it( 'validates workflow definitions, input, output, and invocation options', () => {
|
|
54
54
|
expect( () => WorkflowValidator.validateDefinition( workflowArgs ) ).not.toThrow();
|
|
55
55
|
const validator = new WorkflowValidator( workflowArgs );
|
|
56
|
+
const input = { value: 'ok' };
|
|
57
|
+
const output = { result: 'ok' };
|
|
56
58
|
|
|
57
|
-
expect(
|
|
58
|
-
expect(
|
|
59
|
-
expect( () => validator.
|
|
59
|
+
expect( validator.parseInput( input ) ).toEqual( input );
|
|
60
|
+
expect( validator.parseOutput( output ) ).toEqual( output );
|
|
61
|
+
expect( () => validator.parseInvocationOptions( {
|
|
60
62
|
detached: true,
|
|
61
63
|
activityOptions: { retry: { maximumAttempts: 1 } },
|
|
62
64
|
context: { info: { workflowId: 'test-workflow' } }
|
|
@@ -71,10 +73,10 @@ describe( 'interface validators', () => {
|
|
|
71
73
|
|
|
72
74
|
const validator = new WorkflowValidator( workflowArgs );
|
|
73
75
|
|
|
74
|
-
expect( () => validator.
|
|
75
|
-
expect( () => validator.
|
|
76
|
-
expect( () => validator.
|
|
77
|
-
expect( () => validator.
|
|
76
|
+
expect( () => validator.parseInput( { value: 1 } ) ).toThrow( ValidationError );
|
|
77
|
+
expect( () => validator.parseInput( { value: 1 } ) ).toThrow( /Workflow "valid_workflow" input validation failed/ );
|
|
78
|
+
expect( () => validator.parseOutput( { result: 1 } ) ).toThrow( /Workflow "valid_workflow" output validation failed/ );
|
|
79
|
+
expect( () => validator.parseInvocationOptions( {
|
|
78
80
|
options: { activityOptions: { retry: { maximumAttempts: 1 } } }
|
|
79
81
|
} ) ).toThrow( /Workflow "valid_workflow" invocation options validation failed/ );
|
|
80
82
|
} );
|
|
@@ -84,9 +86,11 @@ describe( 'interface validators', () => {
|
|
|
84
86
|
name: 'schema_less_workflow',
|
|
85
87
|
fn
|
|
86
88
|
} );
|
|
89
|
+
const input = { anything: true };
|
|
90
|
+
const output = { anything: true };
|
|
87
91
|
|
|
88
|
-
expect(
|
|
89
|
-
expect(
|
|
92
|
+
expect( validator.parseInput( input ) ).toBe( input );
|
|
93
|
+
expect( validator.parseOutput( output ) ).toBe( output );
|
|
90
94
|
} );
|
|
91
95
|
} );
|
|
92
96
|
|
|
@@ -94,9 +98,11 @@ describe( 'interface validators', () => {
|
|
|
94
98
|
it( 'validates step definitions, input, and output', () => {
|
|
95
99
|
expect( () => StepValidator.validateDefinition( stepArgs ) ).not.toThrow();
|
|
96
100
|
const validator = new StepValidator( stepArgs );
|
|
101
|
+
const input = { value: 'ok' };
|
|
102
|
+
const output = { result: 'ok' };
|
|
97
103
|
|
|
98
|
-
expect(
|
|
99
|
-
expect(
|
|
104
|
+
expect( validator.parseInput( input ) ).toEqual( input );
|
|
105
|
+
expect( validator.parseOutput( output ) ).toEqual( output );
|
|
100
106
|
} );
|
|
101
107
|
|
|
102
108
|
it( 'throws ValidationError with useful prefixes for invalid step data', () => {
|
|
@@ -106,8 +112,8 @@ describe( 'interface validators', () => {
|
|
|
106
112
|
|
|
107
113
|
const validator = new StepValidator( stepArgs );
|
|
108
114
|
|
|
109
|
-
expect( () => validator.
|
|
110
|
-
expect( () => validator.
|
|
115
|
+
expect( () => validator.parseInput( { value: 1 } ) ).toThrow( /Step "valid_step" input validation failed/ );
|
|
116
|
+
expect( () => validator.parseOutput( { result: 1 } ) ).toThrow( /Step "valid_step" output validation failed/ );
|
|
111
117
|
} );
|
|
112
118
|
} );
|
|
113
119
|
|
|
@@ -115,9 +121,11 @@ describe( 'interface validators', () => {
|
|
|
115
121
|
it( 'validates evaluator definitions, input, and EvaluationResult output', () => {
|
|
116
122
|
expect( () => EvaluatorValidator.validateDefinition( evaluatorArgs ) ).not.toThrow();
|
|
117
123
|
const validator = new EvaluatorValidator( evaluatorArgs );
|
|
124
|
+
const input = { value: 'ok' };
|
|
125
|
+
const output = new EvaluationResult( { value: 'pass', confidence: 1 } );
|
|
118
126
|
|
|
119
|
-
expect(
|
|
120
|
-
expect(
|
|
127
|
+
expect( validator.parseInput( input ) ).toEqual( input );
|
|
128
|
+
expect( validator.parseOutput( output ) ).toEqual( output );
|
|
121
129
|
} );
|
|
122
130
|
|
|
123
131
|
it( 'throws ValidationError for output schemas and invalid evaluator output', () => {
|
|
@@ -128,8 +136,8 @@ describe( 'interface validators', () => {
|
|
|
128
136
|
|
|
129
137
|
const validator = new EvaluatorValidator( evaluatorArgs );
|
|
130
138
|
|
|
131
|
-
expect( () => validator.
|
|
132
|
-
expect( () => validator.
|
|
139
|
+
expect( () => validator.parseInput( { value: 1 } ) ).toThrow( /Evaluator "valid_evaluator" input validation failed/ );
|
|
140
|
+
expect( () => validator.parseOutput( { value: 'pass', confidence: 1 } ) ).toThrow(
|
|
133
141
|
/Evaluator "valid_evaluator" output validation failed/
|
|
134
142
|
);
|
|
135
143
|
} );
|
|
@@ -21,6 +21,8 @@ export type WorkflowContext<
|
|
|
21
21
|
* It acts as a checkpoint when the workflow gets too long or approaches certain scaling limits.
|
|
22
22
|
*
|
|
23
23
|
* It accepts input with the same schema as the parent workflow function (`inputSchema`).
|
|
24
|
+
* The next run parses that input like any other workflow start; pass wire-format values if
|
|
25
|
+
* `inputSchema` transforms are not safe to apply twice.
|
|
24
26
|
*
|
|
25
27
|
* Calling this function must be the last statement in the workflow, accompanied by a `return`:
|
|
26
28
|
*
|
|
@@ -38,7 +40,7 @@ export type WorkflowContext<
|
|
|
38
40
|
* @returns The workflow output type for type-checking; never returns at runtime.
|
|
39
41
|
*/
|
|
40
42
|
continueAsNew: InputSchema extends AnyZodSchema ?
|
|
41
|
-
( input: z.
|
|
43
|
+
( input: z.input<InputSchema> ) => ( OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void ) :
|
|
42
44
|
() => ( OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void ),
|
|
43
45
|
|
|
44
46
|
/**
|
|
@@ -122,39 +124,44 @@ export type WorkflowOptions = {
|
|
|
122
124
|
/**
|
|
123
125
|
* The handler function of a workflow.
|
|
124
126
|
*
|
|
125
|
-
* @param input -
|
|
127
|
+
* @param input - Parsed workflow input (`z.infer<inputSchema>`).
|
|
126
128
|
* @param context - A context object with tools and information.
|
|
127
129
|
*
|
|
128
|
-
* @returns A value
|
|
130
|
+
* @returns A value accepted by `outputSchema` before parse (`z.input<outputSchema>`).
|
|
129
131
|
*/
|
|
130
132
|
export type WorkflowFunction<
|
|
131
133
|
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
132
134
|
OutputSchema extends AnyZodSchema | undefined = undefined
|
|
133
135
|
> = InputSchema extends AnyZodSchema ?
|
|
134
136
|
( input: z.infer<InputSchema>, context: WorkflowContext<InputSchema, OutputSchema> ) =>
|
|
135
|
-
Promise<OutputSchema extends AnyZodSchema ? z.
|
|
137
|
+
Promise<OutputSchema extends AnyZodSchema ? z.input<OutputSchema> : void> :
|
|
136
138
|
( input: undefined | null, context: WorkflowContext<InputSchema, OutputSchema> ) =>
|
|
137
|
-
Promise<OutputSchema extends AnyZodSchema ? z.
|
|
139
|
+
Promise<OutputSchema extends AnyZodSchema ? z.input<OutputSchema> : void>;
|
|
138
140
|
|
|
139
141
|
/**
|
|
140
142
|
* A wrapper around the user defined `fn` handler function.
|
|
141
143
|
*
|
|
142
|
-
*
|
|
144
|
+
* Callers pass values accepted by `inputSchema` (`z.input`). The wrapper parses input,
|
|
145
|
+
* invokes `fn`, parses output, and returns `z.infer<outputSchema>`.
|
|
143
146
|
*
|
|
144
147
|
* The second argument is a WorkflowInvocationOptions object, allowing workflows configuration overwrite.
|
|
145
148
|
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* @param input - The workflow input; it matches the schema defined by `inputSchema`.
|
|
149
|
+
* @param input - The workflow input before `inputSchema` parse.
|
|
149
150
|
* @param options - Additional options for the invocation.
|
|
150
|
-
* @returns
|
|
151
|
+
* @returns The workflow output after `outputSchema` parse.
|
|
151
152
|
*/
|
|
152
|
-
export type WorkflowFunctionWrapper<
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
export type WorkflowFunctionWrapper<
|
|
154
|
+
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
155
|
+
OutputSchema extends AnyZodSchema | undefined = undefined
|
|
156
|
+
> = InputSchema extends AnyZodSchema ?
|
|
157
|
+
(
|
|
158
|
+
input: z.input<InputSchema>,
|
|
159
|
+
options?: WorkflowInvocationOptions
|
|
160
|
+
) => Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void> :
|
|
161
|
+
(
|
|
162
|
+
input?: undefined | null,
|
|
163
|
+
options?: WorkflowInvocationOptions
|
|
164
|
+
) => Promise<OutputSchema extends AnyZodSchema ? z.infer<OutputSchema> : void>;
|
|
158
165
|
|
|
159
166
|
/**
|
|
160
167
|
* Creates a workflow.
|
|
@@ -270,7 +277,7 @@ export type WorkflowFunctionWrapper<WorkflowFunction extends ( ...args: any ) =>
|
|
|
270
277
|
* @param params.outputSchema - Zod schema for workflow output
|
|
271
278
|
* @param params.fn - A function containing the workflow code
|
|
272
279
|
* @param params.options - Optional workflow options.
|
|
273
|
-
* @returns
|
|
280
|
+
* @returns A wrapper that parses input/output around `fn`
|
|
274
281
|
*/
|
|
275
282
|
export declare function workflow<
|
|
276
283
|
InputSchema extends AnyZodSchema | undefined = undefined,
|
|
@@ -288,4 +295,4 @@ export declare function workflow<
|
|
|
288
295
|
* while maintaining backward compatibility with existing callers.
|
|
289
296
|
*/
|
|
290
297
|
aliases?: string[];
|
|
291
|
-
} ): WorkflowFunctionWrapper<
|
|
298
|
+
} ): WorkflowFunctionWrapper<InputSchema, OutputSchema>;
|
|
@@ -10,17 +10,6 @@ import { createWorkflow } from '#helpers/component';
|
|
|
10
10
|
import { FatalError } from '#errors';
|
|
11
11
|
import * as C from '#consts';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* Execute the workflow without temporal, using the fn handler function.
|
|
15
|
-
* This is important to allow for workflows to have unit tests
|
|
16
|
-
*/
|
|
17
|
-
const executeWithoutTemporal = async ( { input, validator, handler, contextOverrides = {} } ) => {
|
|
18
|
-
validator.validateInput( input );
|
|
19
|
-
const output = await handler( input, deepMerge( WorkflowContext.build(), contextOverrides ) );
|
|
20
|
-
validator.validateOutput( output );
|
|
21
|
-
return output;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
13
|
/**
|
|
25
14
|
* Add a global dispatcher function to be used to invoke activities.
|
|
26
15
|
* This will replace direct activity invocation in the user code by the webpack loader.
|
|
@@ -51,12 +40,14 @@ export function workflow( { name, description, inputSchema, outputSchema, fn, op
|
|
|
51
40
|
const disableTrace = options.disableTrace ?? defaultOptions.disableTrace;
|
|
52
41
|
const validator = new WorkflowValidator( { name, inputSchema, outputSchema } );
|
|
53
42
|
|
|
54
|
-
const handler = async ( input,
|
|
55
|
-
validator.
|
|
43
|
+
const handler = async ( input, rawInvocationOptions = {} ) => {
|
|
44
|
+
const invocationOptions = validator.parseInvocationOptions( rawInvocationOptions );
|
|
56
45
|
|
|
57
46
|
// If called outside Temporal workflow context, just execute the handler function
|
|
58
47
|
if ( !inWorkflowContext() ) {
|
|
59
|
-
return
|
|
48
|
+
return validator.parseOutput(
|
|
49
|
+
await fn( validator.parseInput( input ), deepMerge( WorkflowContext.build(), invocationOptions?.context ) )
|
|
50
|
+
);
|
|
60
51
|
}
|
|
61
52
|
|
|
62
53
|
const { workflowId, runId, memo, root } = workflowInfo();
|
|
@@ -99,9 +90,7 @@ export function workflow( { name, description, inputSchema, outputSchema, fn, op
|
|
|
99
90
|
};
|
|
100
91
|
|
|
101
92
|
try {
|
|
102
|
-
validator.
|
|
103
|
-
const output = await fn( input, WorkflowContext.build() );
|
|
104
|
-
validator.validateOutput( output );
|
|
93
|
+
const output = validator.parseOutput( await fn( validator.parseInput( input ), WorkflowContext.build() ) );
|
|
105
94
|
|
|
106
95
|
return { [C.WORKFLOW_WRAPPER_VERSION_FIELD]: 1, output, ...traceDestinations };
|
|
107
96
|
} catch ( error ) {
|
|
@@ -15,9 +15,9 @@ const executeChildMock = vi.hoisted( () => vi.fn() );
|
|
|
15
15
|
const workflowInfoMock = vi.hoisted( () => vi.fn() );
|
|
16
16
|
const continueAsNewMock = vi.hoisted( () => vi.fn() );
|
|
17
17
|
const validateDefinitionMock = vi.hoisted( () => vi.fn() );
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
18
|
+
const parseInputMock = vi.hoisted( () => vi.fn( input => input ) );
|
|
19
|
+
const parseOutputMock = vi.hoisted( () => vi.fn( output => output ) );
|
|
20
|
+
const parseInvocationOptionsMock = vi.hoisted( () => vi.fn( options => options ) );
|
|
21
21
|
const validatorConstructorMock = vi.hoisted( () => vi.fn() );
|
|
22
22
|
const createWorkflowMock = vi.hoisted( () => vi.fn( ( { handler } ) => handler ) );
|
|
23
23
|
|
|
@@ -29,9 +29,9 @@ vi.mock( './validations/index.js', () => {
|
|
|
29
29
|
|
|
30
30
|
constructor( ...args ) {
|
|
31
31
|
validatorConstructorMock( ...args );
|
|
32
|
-
this.
|
|
33
|
-
this.
|
|
34
|
-
this.
|
|
32
|
+
this.parseInput = parseInputMock;
|
|
33
|
+
this.parseOutput = parseOutputMock;
|
|
34
|
+
this.parseInvocationOptions = parseInvocationOptionsMock;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -210,9 +210,9 @@ describe( 'workflow()', () => {
|
|
|
210
210
|
value: 'test-workflow-ok',
|
|
211
211
|
extra: 'custom'
|
|
212
212
|
} );
|
|
213
|
-
expect(
|
|
214
|
-
expect(
|
|
215
|
-
expect(
|
|
213
|
+
expect( parseInvocationOptionsMock ).toHaveBeenCalledWith( { context: { extra: 'custom' } } );
|
|
214
|
+
expect( parseInputMock ).toHaveBeenCalledWith( { suffix: '-ok' } );
|
|
215
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( { value: 'test-workflow-ok', extra: 'custom' } );
|
|
216
216
|
expect( workflowInfoMock ).not.toHaveBeenCalled();
|
|
217
217
|
expect( proxyActivitiesMock ).not.toHaveBeenCalled();
|
|
218
218
|
} );
|
|
@@ -220,7 +220,7 @@ describe( 'workflow()', () => {
|
|
|
220
220
|
it( 'does not run fn when plain function input validation fails', async () => {
|
|
221
221
|
const { workflow } = await import( './workflow.js' );
|
|
222
222
|
const error = new ValidationError( 'invalid input' );
|
|
223
|
-
|
|
223
|
+
parseInputMock.mockImplementationOnce( () => {
|
|
224
224
|
throw error;
|
|
225
225
|
} );
|
|
226
226
|
const fn = vi.fn();
|
|
@@ -234,13 +234,13 @@ describe( 'workflow()', () => {
|
|
|
234
234
|
|
|
235
235
|
await expect( wf( { value: 1 } ) ).rejects.toBe( error );
|
|
236
236
|
expect( fn ).not.toHaveBeenCalled();
|
|
237
|
-
expect(
|
|
237
|
+
expect( parseOutputMock ).not.toHaveBeenCalled();
|
|
238
238
|
} );
|
|
239
239
|
|
|
240
240
|
it( 'propagates plain function output validation errors after fn runs', async () => {
|
|
241
241
|
const { workflow } = await import( './workflow.js' );
|
|
242
242
|
const error = new ValidationError( 'invalid output' );
|
|
243
|
-
|
|
243
|
+
parseOutputMock.mockImplementationOnce( () => {
|
|
244
244
|
throw error;
|
|
245
245
|
} );
|
|
246
246
|
const output = { result: 1 };
|
|
@@ -257,7 +257,7 @@ describe( 'workflow()', () => {
|
|
|
257
257
|
expect( fn ).toHaveBeenCalledWith( { value: 'ok' }, expect.objectContaining( {
|
|
258
258
|
info: { workflowId: 'test-workflow', runId: 'test-run' }
|
|
259
259
|
} ) );
|
|
260
|
-
expect(
|
|
260
|
+
expect( parseOutputMock ).toHaveBeenCalledWith( output );
|
|
261
261
|
} );
|
|
262
262
|
} );
|
|
263
263
|
|
|
@@ -294,7 +294,7 @@ describe( 'workflow()', () => {
|
|
|
294
294
|
retry: { maximumAttempts: 7 }
|
|
295
295
|
}
|
|
296
296
|
} ) ).resolves.toEqual( { child: 'ok' } );
|
|
297
|
-
expect(
|
|
297
|
+
expect( parseInvocationOptionsMock ).toHaveBeenCalledWith( {
|
|
298
298
|
detached: true,
|
|
299
299
|
context: { testOnly: true },
|
|
300
300
|
activityOptions: {
|
|
@@ -543,7 +543,7 @@ describe( 'workflow()', () => {
|
|
|
543
543
|
fn: async () => ( { result: 1 } )
|
|
544
544
|
} ) );
|
|
545
545
|
|
|
546
|
-
|
|
546
|
+
parseInputMock.mockImplementationOnce( () => {
|
|
547
547
|
throw inputError;
|
|
548
548
|
} );
|
|
549
549
|
await expect( wf( { value: 1 } ) ).rejects.toBe( inputError );
|
|
@@ -551,7 +551,7 @@ describe( 'workflow()', () => {
|
|
|
551
551
|
|
|
552
552
|
delete globalThis[INVOKE_ACTIVITY_SYMBOL];
|
|
553
553
|
setWorkflowInfo( { workflowType: 'runtime_validation_wf', memo: {} } );
|
|
554
|
-
|
|
554
|
+
parseOutputMock.mockImplementationOnce( () => {
|
|
555
555
|
throw outputError;
|
|
556
556
|
} );
|
|
557
557
|
await expect( wf( { value: 'ok' } ) ).rejects.toBe( outputError );
|