@outputai/core 0.10.1-next.fc0a41f.0 → 0.11.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 +12 -7
- package/src/consts.js +5 -2
- package/src/errors.d.ts +12 -2
- package/src/errors.js +17 -3
- package/src/errors.spec.js +19 -0
- package/src/helpers/activity_options.js +21 -0
- package/src/helpers/activity_options.spec.js +55 -0
- package/src/helpers/error_serializer.js +236 -0
- package/src/helpers/error_serializer.spec.js +413 -0
- package/src/helpers/errors.js +55 -13
- package/src/helpers/errors.spec.js +166 -66
- package/src/helpers/fetch.js +1 -38
- package/src/helpers/fetch.spec.js +1 -39
- package/src/helpers/function.js +13 -0
- package/src/helpers/function.spec.js +23 -1
- package/src/helpers/redact.js +51 -0
- package/src/helpers/redact.spec.js +66 -0
- package/src/helpers/string.js +10 -0
- package/src/helpers/string.spec.js +36 -1
- package/src/hooks/index.d.ts +35 -44
- package/src/hooks/index.js +2 -1
- package/src/hooks/index.spec.js +15 -7
- 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/webhook.js +1 -2
- package/src/interface/webhook.spec.js +25 -19
- package/src/interface/workflow.d.ts +25 -18
- package/src/interface/workflow.js +24 -52
- package/src/interface/workflow.spec.js +109 -108
- package/src/interface/workflow_activity_options.js +2 -5
- package/src/temporal/client.js +47 -0
- package/src/temporal/client.spec.js +124 -0
- package/src/temporal/index.d.ts +32 -0
- package/src/temporal/index.js +1 -0
- package/src/tracing/trace_engine.js +9 -11
- package/src/tracing/trace_engine.spec.js +67 -28
- package/src/worker/catalog_workflow/catalog_job.js +3 -3
- package/src/worker/catalog_workflow/catalog_job.spec.js +1 -1
- package/src/worker/index.js +10 -1
- package/src/worker/index.spec.js +23 -8
- package/src/worker/interceptors/activity.js +32 -19
- package/src/worker/interceptors/activity.spec.js +71 -166
- package/src/worker/interceptors/workflow.js +53 -11
- package/src/worker/interceptors/workflow.spec.js +176 -49
- package/src/worker/loader/workflows.js +16 -3
- package/src/worker/loader/workflows.spec.js +37 -5
- package/src/worker/log_hooks.js +13 -10
- package/src/worker/log_hooks.spec.js +12 -4
- package/src/worker/sinks.js +3 -1
- package/src/worker/sinks.spec.js +21 -4
- package/src/worker/telemetry.js +2 -1
- package/src/worker/temporal_logger.js +33 -0
- package/src/worker/temporal_logger.spec.js +90 -0
- package/src/helpers/aggregations.js +0 -44
- package/src/helpers/aggregations.spec.js +0 -92
- package/src/tracing/tools/utils.js +0 -42
- package/src/tracing/tools/utils.spec.js +0 -83
package/src/hooks/index.d.ts
CHANGED
|
@@ -79,25 +79,6 @@ export interface WorkflowDetails {
|
|
|
79
79
|
attempt: number;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
/**
|
|
83
|
-
* Attribute totals collected while an activity executes.
|
|
84
|
-
*/
|
|
85
|
-
export interface Aggregations {
|
|
86
|
-
/** Cost totals collected from HTTP request cost and LLM usage attributes. */
|
|
87
|
-
cost: {
|
|
88
|
-
total: number;
|
|
89
|
-
};
|
|
90
|
-
/** Token totals collected from LLM usage attributes. */
|
|
91
|
-
tokens: {
|
|
92
|
-
[tokenType: string]: number | undefined;
|
|
93
|
-
total: number;
|
|
94
|
-
};
|
|
95
|
-
/** HTTP request count totals. */
|
|
96
|
-
httpRequests: {
|
|
97
|
-
total: number;
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
82
|
/**
|
|
102
83
|
* Common hook payload fields
|
|
103
84
|
*/
|
|
@@ -108,24 +89,6 @@ export interface HookPayloadBase {
|
|
|
108
89
|
eventDate: number;
|
|
109
90
|
}
|
|
110
91
|
|
|
111
|
-
/**
|
|
112
|
-
* Payload passed to the onError() handler when a workflow, activity or runtime error occurs.
|
|
113
|
-
*/
|
|
114
|
-
export interface ErrorHookPayload extends HookPayloadBase {
|
|
115
|
-
/** Origin of the error: workflow execution, activity execution, or runtime. */
|
|
116
|
-
source: 'workflow' | 'activity' | 'runtime';
|
|
117
|
-
/** Information about the current workflow execution */
|
|
118
|
-
workflowDetails?: WorkflowDetails;
|
|
119
|
-
/** Temporal's activityInfo(). If source is activity */
|
|
120
|
-
activityInfo?: Info;
|
|
121
|
-
/** Output component kind for the activity, e.g. step, evaluator, or internal_step. */
|
|
122
|
-
outputActivityKind: string;
|
|
123
|
-
/** Attribute totals collected during the activity execution. */
|
|
124
|
-
aggregations?: Aggregations | null;
|
|
125
|
-
/** The error thrown. */
|
|
126
|
-
error: Error;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
92
|
/**
|
|
130
93
|
* Common hook payload fields for events associated with an workflow.
|
|
131
94
|
*/
|
|
@@ -148,7 +111,13 @@ export type WorkflowEndHookPayload = WorkflowPayloadBase;
|
|
|
148
111
|
* Payload passed to the onWorkflowError() handler when a workflow run fails.
|
|
149
112
|
*/
|
|
150
113
|
export interface WorkflowErrorHookPayload extends WorkflowPayloadBase {
|
|
151
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Workflow error rehydrated after crossing the Temporal sandbox boundary.
|
|
116
|
+
* Preserves `name`, `message`, `cause`, and other enumerable properties from
|
|
117
|
+
* the serialized form; it is a reconstructed `Error`, not the original class.
|
|
118
|
+
* Extra fields are present at runtime but not on the `Error` type — narrow or
|
|
119
|
+
* cast in TypeScript when you need them.
|
|
120
|
+
*/
|
|
152
121
|
error: Error;
|
|
153
122
|
}
|
|
154
123
|
|
|
@@ -172,21 +141,43 @@ export type ActivityStartHookPayload = ActivityPayloadBase;
|
|
|
172
141
|
/**
|
|
173
142
|
* Payload passed to the onActivityEnd() handler when an activity completes successfully.
|
|
174
143
|
*/
|
|
175
|
-
export
|
|
176
|
-
/** Attribute totals collected during the activity execution. */
|
|
177
|
-
aggregations: Aggregations | null;
|
|
178
|
-
}
|
|
144
|
+
export type ActivityEndHookPayload = ActivityPayloadBase;
|
|
179
145
|
|
|
180
146
|
/**
|
|
181
147
|
* Payload passed to the onActivityError() handler when an activity fails.
|
|
182
148
|
*/
|
|
183
149
|
export interface ActivityErrorHookPayload extends ActivityPayloadBase {
|
|
184
|
-
/** Attribute totals collected during the activity execution. */
|
|
185
|
-
aggregations: Aggregations | null;
|
|
186
150
|
/** The error thrown. */
|
|
187
151
|
error: Error;
|
|
188
152
|
}
|
|
189
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Payload passed to the onError() handler when a workflow, activity or runtime error occurs.
|
|
156
|
+
*/
|
|
157
|
+
export type ErrorHookPayload =
|
|
158
|
+
( WorkflowPayloadBase & {
|
|
159
|
+
/** Workflow error origin. */
|
|
160
|
+
source: 'workflow';
|
|
161
|
+
/**
|
|
162
|
+
* Workflow error rehydrated after crossing the Temporal sandbox boundary.
|
|
163
|
+
* Preserves `name`, `message`, `cause`, and other enumerable properties from
|
|
164
|
+
* the serialized form; it is a reconstructed `Error`, not the original class.
|
|
165
|
+
* Extra fields are present at runtime but not on the `Error` type — narrow or
|
|
166
|
+
* cast in TypeScript when you need them.
|
|
167
|
+
*/
|
|
168
|
+
error: Error;
|
|
169
|
+
} ) |
|
|
170
|
+
( ActivityErrorHookPayload & {
|
|
171
|
+
/** Activity error origin. */
|
|
172
|
+
source: 'activity';
|
|
173
|
+
} ) |
|
|
174
|
+
( HookPayloadBase & {
|
|
175
|
+
/** Worker runtime error origin. */
|
|
176
|
+
source: 'runtime';
|
|
177
|
+
/** The original error instance. */
|
|
178
|
+
error: Error;
|
|
179
|
+
} );
|
|
180
|
+
|
|
190
181
|
/**
|
|
191
182
|
* Register a handler to be invoked on workflow, activity or runtime errors.
|
|
192
183
|
*
|
package/src/hooks/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mainEventBus, stepEventBus } from '#bus';
|
|
2
2
|
import { BusEventType } from '#consts';
|
|
3
|
+
import { serializeError } from '#helpers/error_serializer';
|
|
3
4
|
import { createChildLogger } from '#logger';
|
|
4
5
|
import { pendingHooks } from './pending_hooks.js';
|
|
5
6
|
|
|
@@ -15,7 +16,7 @@ const log = createChildLogger( 'Hooks' );
|
|
|
15
16
|
const callHookCb = ( fn, args, hookName ) => {
|
|
16
17
|
const promise = Promise.resolve()
|
|
17
18
|
.then( () => fn( args ) )
|
|
18
|
-
.catch( e => log.error( `${hookName} hook error`, {
|
|
19
|
+
.catch( e => log.error( `${hookName} hook error`, { error: serializeError( e ) } ) )
|
|
19
20
|
.finally( () => pendingHooks.delete( promise ) );
|
|
20
21
|
pendingHooks.add( promise );
|
|
21
22
|
return promise;
|
package/src/hooks/index.spec.js
CHANGED
|
@@ -5,6 +5,11 @@ const logErrorMock = vi.hoisted( () => vi.fn() );
|
|
|
5
5
|
const createChildLoggerMock = vi.hoisted( () =>
|
|
6
6
|
vi.fn( () => ( { error: logErrorMock } ) )
|
|
7
7
|
);
|
|
8
|
+
const serializeErrorMock = vi.hoisted( () => vi.fn( error => ( {
|
|
9
|
+
name: error.name,
|
|
10
|
+
message: error.message,
|
|
11
|
+
stack: error.stack
|
|
12
|
+
} ) ) );
|
|
8
13
|
|
|
9
14
|
const mainOnHandlers = vi.hoisted( () => ( {} ) );
|
|
10
15
|
const stepOnHandlers = vi.hoisted( () => ( {} ) );
|
|
@@ -21,6 +26,7 @@ const stepEventBusMock = vi.hoisted( () => ( {
|
|
|
21
26
|
} ) );
|
|
22
27
|
|
|
23
28
|
vi.mock( '#logger', () => ( { createChildLogger: createChildLoggerMock } ) );
|
|
29
|
+
vi.mock( '#helpers/error_serializer', () => ( { serializeError: serializeErrorMock } ) );
|
|
24
30
|
vi.mock( '#bus', () => ( {
|
|
25
31
|
mainEventBus: mainEventBusMock,
|
|
26
32
|
stepEventBus: stepEventBusMock
|
|
@@ -59,12 +65,6 @@ const activityInfo = {
|
|
|
59
65
|
|
|
60
66
|
const eventDate = 1710000001234;
|
|
61
67
|
|
|
62
|
-
const aggregations = {
|
|
63
|
-
cost: { total: 0 },
|
|
64
|
-
tokens: { total: 0 },
|
|
65
|
-
httpRequests: { total: 1 }
|
|
66
|
-
};
|
|
67
|
-
|
|
68
68
|
describe( 'hooks/index', () => {
|
|
69
69
|
beforeEach( () => {
|
|
70
70
|
vi.clearAllMocks();
|
|
@@ -145,6 +145,14 @@ describe( 'hooks/index', () => {
|
|
|
145
145
|
await mainOnHandlers[BusEventType.RUNTIME_ERROR]( { eventId: 'evt-rt-1', eventDate, error } );
|
|
146
146
|
|
|
147
147
|
expect( handler ).toHaveBeenCalledWith( { eventId: 'evt-rt-1', eventDate, source: 'runtime', error } );
|
|
148
|
+
expect( logErrorMock ).toHaveBeenCalledWith( 'onError hook error', {
|
|
149
|
+
error: {
|
|
150
|
+
name: 'Error',
|
|
151
|
+
message: 'boom',
|
|
152
|
+
stack: expect.any( String )
|
|
153
|
+
}
|
|
154
|
+
} );
|
|
155
|
+
expect( serializeErrorMock ).toHaveBeenCalledWith( expect.objectContaining( { message: 'boom' } ) );
|
|
148
156
|
} );
|
|
149
157
|
} );
|
|
150
158
|
|
|
@@ -205,7 +213,7 @@ describe( 'hooks/index', () => {
|
|
|
205
213
|
describe( 'activity lifecycle hooks', () => {
|
|
206
214
|
const cases = [
|
|
207
215
|
[ 'onActivityStart', onActivityStart, BusEventType.ACTIVITY_START, undefined ],
|
|
208
|
-
[ 'onActivityEnd', onActivityEnd, BusEventType.ACTIVITY_END
|
|
216
|
+
[ 'onActivityEnd', onActivityEnd, BusEventType.ACTIVITY_END ],
|
|
209
217
|
[ 'onActivityError', onActivityError, BusEventType.ACTIVITY_ERROR, { error: new Error( 'activity failed' ) } ]
|
|
210
218
|
];
|
|
211
219
|
|
|
@@ -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
|
};
|