@output.ai/core 0.2.2 → 0.2.4
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/bin/worker.sh +1 -1
- package/package.json +1 -1
- package/src/interface/evaluator.js +3 -0
- package/src/worker/catalog_workflow/workflow.js +3 -2
- package/src/worker/index.js +36 -2
- package/src/worker/webpack_loaders/consts.js +0 -13
- package/src/worker/webpack_loaders/tools.js +1 -6
- package/src/worker/webpack_loaders/tools.spec.js +0 -44
- package/src/worker/webpack_loaders/workflow_validator/index.mjs +3 -19
- package/src/worker/webpack_loaders/workflow_validator/index.spec.js +0 -44
package/bin/worker.sh
CHANGED
package/package.json
CHANGED
|
@@ -81,6 +81,7 @@ export class EvaluationStringResult extends EvaluationResult {
|
|
|
81
81
|
* @param {number} args.confidence - The confidence on the evaluation
|
|
82
82
|
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
83
83
|
*/
|
|
84
|
+
// eslint-disable-next-line no-useless-constructor
|
|
84
85
|
constructor( args ) {
|
|
85
86
|
super( args );
|
|
86
87
|
}
|
|
@@ -103,6 +104,7 @@ export class EvaluationBooleanResult extends EvaluationResult {
|
|
|
103
104
|
* @param {number} args.confidence - The confidence on the evaluation
|
|
104
105
|
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
105
106
|
*/
|
|
107
|
+
// eslint-disable-next-line no-useless-constructor
|
|
106
108
|
constructor( args ) {
|
|
107
109
|
super( args );
|
|
108
110
|
}
|
|
@@ -125,6 +127,7 @@ export class EvaluationNumberResult extends EvaluationResult {
|
|
|
125
127
|
* @param {number} args.confidence - The confidence on the evaluation
|
|
126
128
|
* @param {string} [args.reasoning] - The reasoning behind the result
|
|
127
129
|
*/
|
|
130
|
+
// eslint-disable-next-line no-useless-constructor
|
|
128
131
|
constructor( args ) {
|
|
129
132
|
super( args );
|
|
130
133
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineQuery, setHandler } from '@temporalio/workflow';
|
|
1
|
+
import { defineQuery, setHandler, condition } from '@temporalio/workflow';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* This is a special workflow, unique to each worker, which holds the meta information of all other workflows in that worker.
|
|
@@ -9,5 +9,6 @@ import { defineQuery, setHandler } from '@temporalio/workflow';
|
|
|
9
9
|
*/
|
|
10
10
|
export default async function catalogWorkflow( catalog ) {
|
|
11
11
|
setHandler( defineQuery( 'get' ), () => catalog );
|
|
12
|
-
|
|
12
|
+
// Wait indefinitely but remain responsive to workflow cancellation
|
|
13
|
+
await condition( () => false );
|
|
13
14
|
};
|
package/src/worker/index.js
CHANGED
|
@@ -56,5 +56,39 @@ const callerDir = process.argv[2];
|
|
|
56
56
|
} );
|
|
57
57
|
|
|
58
58
|
console.log( '[Core]', 'Running worker...' );
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
|
|
60
|
+
// FORCE_QUIT_GRACE_MS delays the second instance of a shutdown command.
|
|
61
|
+
// If running output-worker directly with npx, 2 signals are recieved in
|
|
62
|
+
// rapid succession, and users see the force quit message.
|
|
63
|
+
const FORCE_QUIT_GRACE_MS = 1000;
|
|
64
|
+
const state = { isShuttingDown: false, shutdownStartedAt: null };
|
|
65
|
+
|
|
66
|
+
const shutdown = signal => {
|
|
67
|
+
if ( state.isShuttingDown ) {
|
|
68
|
+
const elapsed = Date.now() - state.shutdownStartedAt;
|
|
69
|
+
if ( elapsed < FORCE_QUIT_GRACE_MS ) {
|
|
70
|
+
return; // ignore rapid duplicate signals
|
|
71
|
+
}
|
|
72
|
+
process.stderr.write( '[Core] Force quitting...\n' );
|
|
73
|
+
process.exit( 1 );
|
|
74
|
+
}
|
|
75
|
+
state.isShuttingDown = true;
|
|
76
|
+
state.shutdownStartedAt = Date.now();
|
|
77
|
+
process.stderr.write( `[Core] Received ${signal}, shutting down...\n` );
|
|
78
|
+
worker.shutdown();
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
process.on( 'SIGTERM', () => shutdown( 'SIGTERM' ) );
|
|
82
|
+
process.on( 'SIGINT', () => shutdown( 'SIGINT' ) );
|
|
83
|
+
|
|
84
|
+
await worker.run();
|
|
85
|
+
process.stderr.write( '[Core] Worker stopped.\n' );
|
|
86
|
+
|
|
87
|
+
await connection.close();
|
|
88
|
+
process.stderr.write( '[Core] Connection closed.\n' );
|
|
89
|
+
|
|
90
|
+
process.exit( 0 );
|
|
91
|
+
} )().catch( error => {
|
|
92
|
+
process.stderr.write( `[Core] Fatal error: ${error.message}\n` );
|
|
93
|
+
process.exit( 1 );
|
|
94
|
+
} );
|
|
@@ -9,19 +9,6 @@ export const ComponentFile = {
|
|
|
9
9
|
WORKFLOW: 'workflow'
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
export const EXTRANEOUS_FILE = 'extraneous';
|
|
13
|
-
export const ExtraneousFileList = [
|
|
14
|
-
'types',
|
|
15
|
-
'consts',
|
|
16
|
-
'constants',
|
|
17
|
-
'vars',
|
|
18
|
-
'variables',
|
|
19
|
-
'utils',
|
|
20
|
-
'tools',
|
|
21
|
-
'functions',
|
|
22
|
-
'shared'
|
|
23
|
-
];
|
|
24
|
-
|
|
25
12
|
export const CoreModule = {
|
|
26
13
|
LOCAL: 'local_core',
|
|
27
14
|
NPM: '@output.ai/core'
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
isExportDefaultDeclaration,
|
|
25
25
|
isFunctionDeclaration
|
|
26
26
|
} from '@babel/types';
|
|
27
|
-
import { ComponentFile,
|
|
27
|
+
import { ComponentFile, NodeType } from './consts.js';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Resolve a relative module specifier against a base directory.
|
|
@@ -138,8 +138,6 @@ export const isWorkflowPath = value => /(^|\/)workflow\.js$/.test( value );
|
|
|
138
138
|
*/
|
|
139
139
|
export const isTypesPath = value => /(^|\/)types\.js$/.test( value );
|
|
140
140
|
|
|
141
|
-
export const isExtraneousFile = value => ExtraneousFileList.map( t => new RegExp( `(^|\/)${t}\\.js$` ) ).find( rx => rx.test( value ) );
|
|
142
|
-
|
|
143
141
|
/**
|
|
144
142
|
* Determine file kind based on its path.
|
|
145
143
|
* @param {string} filename
|
|
@@ -158,9 +156,6 @@ export const getFileKind = path => {
|
|
|
158
156
|
if ( isWorkflowPath( path ) ) {
|
|
159
157
|
return ComponentFile.WORKFLOW;
|
|
160
158
|
}
|
|
161
|
-
if ( isExtraneousFile( path ) ) {
|
|
162
|
-
return EXTRANEOUS_FILE;
|
|
163
|
-
}
|
|
164
159
|
return null;
|
|
165
160
|
};
|
|
166
161
|
|
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
buildSharedStepsNameMap,
|
|
21
21
|
buildWorkflowNameMap,
|
|
22
22
|
buildEvaluatorsNameMap,
|
|
23
|
-
isExtraneousFile,
|
|
24
23
|
getFileKind
|
|
25
24
|
} from './tools.js';
|
|
26
25
|
|
|
@@ -170,49 +169,6 @@ describe( 'workflow_rewriter tools', () => {
|
|
|
170
169
|
expect( isEvaluatorsPath( 'steps.js' ) ).toBe( false );
|
|
171
170
|
} );
|
|
172
171
|
|
|
173
|
-
it( 'isExtraneousFile: returns truthy for extraneous files (types/consts/constants/vars/variables)', () => {
|
|
174
|
-
const ok = [
|
|
175
|
-
'types.js',
|
|
176
|
-
'./types.js',
|
|
177
|
-
'/a/b/types.js',
|
|
178
|
-
'consts.js',
|
|
179
|
-
'./consts.js',
|
|
180
|
-
'/a/b/consts.js',
|
|
181
|
-
'constants.js',
|
|
182
|
-
'./constants.js',
|
|
183
|
-
'/a/b/constants.js',
|
|
184
|
-
'vars.js',
|
|
185
|
-
'./vars.js',
|
|
186
|
-
'/a/b/vars.js',
|
|
187
|
-
'variables.js',
|
|
188
|
-
'./variables.js',
|
|
189
|
-
'/a/b/variables.js'
|
|
190
|
-
];
|
|
191
|
-
for ( const p of ok ) {
|
|
192
|
-
expect( Boolean( isExtraneousFile( p ) ) ).toBe( true );
|
|
193
|
-
}
|
|
194
|
-
} );
|
|
195
|
-
|
|
196
|
-
it( 'isExtraneousFile: returns falsy for non-extraneous or non-.js files', () => {
|
|
197
|
-
const bad = [
|
|
198
|
-
'types.ts',
|
|
199
|
-
'/a/b/types.ts',
|
|
200
|
-
'types.mjs',
|
|
201
|
-
'variables.jsx',
|
|
202
|
-
'variables.mjs',
|
|
203
|
-
'myconstants.js',
|
|
204
|
-
'steps.js',
|
|
205
|
-
'evaluators.js',
|
|
206
|
-
'shared_steps.js',
|
|
207
|
-
'workflow.js',
|
|
208
|
-
'typess.js',
|
|
209
|
-
'/a/b/c/variables.json'
|
|
210
|
-
];
|
|
211
|
-
for ( const p of bad ) {
|
|
212
|
-
expect( isExtraneousFile( p ) ).toBeFalsy();
|
|
213
|
-
}
|
|
214
|
-
} );
|
|
215
|
-
|
|
216
172
|
it( 'createThisMethodCall: builds this.method(\'name\', ...args) call', () => {
|
|
217
173
|
const call = createThisMethodCall( 'invoke', 'n', [ t.numericLiteral( 1 ), t.identifier( 'x' ) ] );
|
|
218
174
|
expect( t.isCallExpression( call ) ).toBe( true );
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import traverseModule from '@babel/traverse';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { parse, toAbsolutePath, getFileKind } from '../tools.js';
|
|
4
|
-
import { ComponentFile
|
|
4
|
+
import { ComponentFile } from '../consts.js';
|
|
5
5
|
import {
|
|
6
6
|
isCallExpression,
|
|
7
7
|
isFunctionExpression,
|
|
@@ -17,19 +17,6 @@ import {
|
|
|
17
17
|
// Handle CJS/ESM interop for Babel packages when executed as a webpack loader
|
|
18
18
|
const traverse = traverseModule.default ?? traverseModule;
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
* Check if workflow dependencies
|
|
22
|
-
*/
|
|
23
|
-
const validateWorkflowImports = ( { specifier, filename } ) => {
|
|
24
|
-
const isCore = Object.values( CoreModule ).includes( specifier );
|
|
25
|
-
const isComponent = Object.values( ComponentFile ).includes( getFileKind( specifier ) );
|
|
26
|
-
const isAllowedExtraneous = getFileKind( specifier ) === EXTRANEOUS_FILE;
|
|
27
|
-
if ( !isCore && !isComponent && !isAllowedExtraneous ) {
|
|
28
|
-
throw new Error( `Invalid dependency in workflow.js: '${specifier}'. \
|
|
29
|
-
Only components (${Object.values( ComponentFile ) } ), @output.ai/core, or whitelisted (${ExtraneousFileList}) imports are allowed in ${filename}` );
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
20
|
/**
|
|
34
21
|
* Check if evaluators, steps or shared_steps import invalid dependencies
|
|
35
22
|
*/
|
|
@@ -41,12 +28,10 @@ Steps, shared_steps, evaluators or workflows are not allowed dependencies in ${f
|
|
|
41
28
|
};
|
|
42
29
|
|
|
43
30
|
/**
|
|
44
|
-
* Validate import for evaluators, steps, shared_steps
|
|
31
|
+
* Validate import for evaluators, steps, shared_steps
|
|
45
32
|
*/
|
|
46
33
|
const executeImportValidations = ( { fileKind, specifier, filename } ) => {
|
|
47
|
-
if ( fileKind
|
|
48
|
-
validateWorkflowImports( { fileKind, specifier, filename } );
|
|
49
|
-
} else if ( Object.values( ComponentFile ).includes( fileKind ) ) {
|
|
34
|
+
if ( Object.values( ComponentFile ).includes( fileKind ) && fileKind !== ComponentFile.WORKFLOW ) {
|
|
50
35
|
validateStepEvaluatorImports( { fileKind, specifier, filename } );
|
|
51
36
|
}
|
|
52
37
|
};
|
|
@@ -62,7 +47,6 @@ const executeImportValidations = ( { fileKind, specifier, filename } ) => {
|
|
|
62
47
|
* - shared_steps.js: may not import evaluators.js, steps.js, shared_steps.js, workflow.js
|
|
63
48
|
* - steps.js: at each step().fn body: calling any evaluator, step, shared_step or workflow is forbidden
|
|
64
49
|
* - steps.js: may not import evaluators.js, steps.js, shared_steps.js, workflow.js
|
|
65
|
-
* - workflow.js: may only import components: evaluators.js, steps.js, shared_steps.js, workflow.js; and files: types.js or `@output.ai/core`
|
|
66
50
|
*
|
|
67
51
|
* @param {string|Buffer} source
|
|
68
52
|
* @param {any} inputMap
|
|
@@ -36,13 +36,6 @@ describe( 'workflow_validator loader', () => {
|
|
|
36
36
|
rmSync( dir, { recursive: true, force: true } );
|
|
37
37
|
} );
|
|
38
38
|
|
|
39
|
-
it( 'workflow.js: rejects external dependencies', async () => {
|
|
40
|
-
const dir = mkdtempSync( join( tmpdir(), 'wf-reject-' ) );
|
|
41
|
-
const src = 'import x from "./foo.js";';
|
|
42
|
-
await expect( runLoader( join( dir, 'workflow.js' ), src ) ).rejects.toThrow( /Invalid (import|dependency) in workflow\.js/ );
|
|
43
|
-
rmSync( dir, { recursive: true, force: true } );
|
|
44
|
-
} );
|
|
45
|
-
|
|
46
39
|
it( 'workflow.js: allows imports from @output.ai/core and local_core', async () => {
|
|
47
40
|
const dir = mkdtempSync( join( tmpdir(), 'wf-allow-external-' ) );
|
|
48
41
|
const src = [
|
|
@@ -125,24 +118,6 @@ describe( 'workflow_validator loader', () => {
|
|
|
125
118
|
rmSync( dir, { recursive: true, force: true } );
|
|
126
119
|
} );
|
|
127
120
|
|
|
128
|
-
it( 'workflow.js: allows require from steps/shared_steps/evaluators/workflow; rejects others', async () => {
|
|
129
|
-
const dir = mkdtempSync( join( tmpdir(), 'wf-req-' ) );
|
|
130
|
-
writeFileSync( join( dir, 'steps.js' ), 'export const S = step({ name: "s" })\n' );
|
|
131
|
-
writeFileSync( join( dir, 'shared_steps.js' ), 'export const SS = step({ name: "ss" })\n' );
|
|
132
|
-
writeFileSync( join( dir, 'evaluators.js' ), 'export const E = evaluator({ name: "e" })\n' );
|
|
133
|
-
writeFileSync( join( dir, 'workflow.js' ), 'export default workflow({ name: "w" })\n' );
|
|
134
|
-
const ok = [
|
|
135
|
-
'const { S } = require("./steps.js");',
|
|
136
|
-
'const { SS } = require("./shared_steps.js");',
|
|
137
|
-
'const { E } = require("./evaluators.js");',
|
|
138
|
-
'const W = require("./workflow.js");'
|
|
139
|
-
].join( '\n' );
|
|
140
|
-
await expect( runLoader( join( dir, 'workflow.js' ), ok ) ).resolves.toBeTruthy();
|
|
141
|
-
const bad = 'const X = require("./util.js");';
|
|
142
|
-
await expect( runLoader( join( dir, 'workflow.js' ), bad ) ).rejects.toThrow( /Invalid (require|dependency) in workflow\.js/ );
|
|
143
|
-
rmSync( dir, { recursive: true, force: true } );
|
|
144
|
-
} );
|
|
145
|
-
|
|
146
121
|
it( 'steps.js: rejects importing shared_steps/evaluators/workflow variants', async () => {
|
|
147
122
|
const dir = mkdtempSync( join( tmpdir(), 'steps-reject2-' ) );
|
|
148
123
|
await expect( runLoader( join( dir, 'steps.js' ), 'import { SS } from "./shared_steps.js";' ) )
|
|
@@ -210,25 +185,6 @@ describe( 'workflow_validator loader', () => {
|
|
|
210
185
|
rmSync( dir, { recursive: true, force: true } );
|
|
211
186
|
} );
|
|
212
187
|
|
|
213
|
-
it( 'workflow.js: allows importing ./types.js and bare types', async () => {
|
|
214
|
-
const dir = mkdtempSync( join( tmpdir(), 'wf-types-allow-' ) );
|
|
215
|
-
writeFileSync( join( dir, 'types.js' ), 'export const T = {}\n' );
|
|
216
|
-
const src1 = 'import { T } from "./types.js";';
|
|
217
|
-
await expect( runLoader( join( dir, 'workflow.js' ), src1 ) ).resolves.toBeTruthy();
|
|
218
|
-
rmSync( dir, { recursive: true, force: true } );
|
|
219
|
-
} );
|
|
220
|
-
|
|
221
|
-
it( 'workflow.js: allows importing extraneous files (consts/constants/vars/variables)', async () => {
|
|
222
|
-
const dir = mkdtempSync( join( tmpdir(), 'wf-extra-allow-' ) );
|
|
223
|
-
const bases = [ 'consts', 'constants', 'vars', 'variables' ];
|
|
224
|
-
for ( const base of bases ) {
|
|
225
|
-
writeFileSync( join( dir, `${base}.js` ), 'export const X = 1\n' );
|
|
226
|
-
const src = `import x from "./${base}.js";`;
|
|
227
|
-
await expect( runLoader( join( dir, 'workflow.js' ), src ) ).resolves.toBeTruthy();
|
|
228
|
-
}
|
|
229
|
-
rmSync( dir, { recursive: true, force: true } );
|
|
230
|
-
} );
|
|
231
|
-
|
|
232
188
|
it( 'steps.js: rejects require of steps/shared_steps/evaluators/workflow; allows other require', async () => {
|
|
233
189
|
const dir = mkdtempSync( join( tmpdir(), 'steps-require-' ) );
|
|
234
190
|
await expect( runLoader( join( dir, 'steps.js' ), 'const { S } = require("./steps.js");' ) )
|