@output.ai/core 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -4,24 +4,30 @@ Provides tools to develop and run a workflow, which is a well defined logical un
4
4
 
5
5
  ## Structure
6
6
 
7
- Workflows are defined using core functions "workflow" and "step", separate files:
7
+ Workflows are defined using core functions ("workflow", "step", "evaluator"), these are defined in separate files, and must be placed within the same folder:
8
8
 
9
9
  ```
10
10
  └ workflows
11
11
  └ example
12
12
  ├ workflow.ts|js <- workflow entry point
13
- ├ steps.ts|js <- file with each step of this workflow
13
+ ├ steps.ts|js <- file containing steps used by the workflow
14
+ ├ evaluators.ts|js <- file containing evaluating functions
14
15
  └ prompt.prompt <- a prompt file
15
16
  └ other-example
16
17
 
17
18
  ```
18
19
 
19
- Think that workflows is the orchestrator and steps are executors. So the workflow only call the steps and the steps call the IO operations, like APIs, DBs, LLMs, etc.
20
+ Workflows are the orchestrator and steps are executors. So the workflow only call the steps and the steps call the IO operations, like APIs, DBs, LLMs, etc. Evaluators are just another different flavor for steps, they work the same, but must return an `EvaluationResult` object.
20
21
 
21
- ## Workflow code
22
+ ## Components
22
23
 
23
- ### workflow.js
24
+ ### Workflow
24
25
 
26
+ The main code, must contain only deterministic orchestration code.
27
+
28
+ File: `workflow.js`
29
+
30
+ Example:
25
31
  ```js
26
32
  import { workflow, z } from '@output.ai/workflow';
27
33
  import { guessByName } from './steps.js';
@@ -42,8 +48,13 @@ export default workflow( {
42
48
  })
43
49
  ```
44
50
 
45
- ### steps.js
51
+ ### Step
52
+
53
+ Re-usable units of work that can contain IO, used by the workflow.
46
54
 
55
+ File: `steps.js`
56
+
57
+ Example:
47
58
  ```js
48
59
  import { api } from './api.js'
49
60
 
@@ -58,9 +69,73 @@ export const guessByName = step( {
58
69
  } )
59
70
  ```
60
71
 
61
- ## webhooks
72
+ ### Shared Steps
73
+
74
+ By default, steps are exclusive to the workflow, so it is not passible to use these steps from elsewhere. In order to have shared steps and make them accessible in different workflows, create a shared steps file. This file can be relatively imported anywhere.
75
+
76
+ File: `shared_steps.js`
77
+
78
+ Example:
79
+ ```js
80
+ export const mySharedStep = step( {
81
+ name: 'mySharedStep',
82
+ ...
83
+ } )
84
+ ```
85
+
86
+ And the usage is the same as any step:
87
+ `workflow.js`
88
+ ```js
89
+ import { mySharedStep } from '../../tools/shared_steps.js'
90
+ ```
91
+
92
+ ### Evaluators
93
+
94
+ Steps that analyze LLM response, or take other measurements are contained in evaluators.
95
+
96
+ File: `evaluators.js`
97
+
98
+ Example:
99
+ ```js
100
+ import { evaluator, EvaluationStringResult } from './api.js'
101
+
102
+ export const judgeResult = evaluator( {
103
+ name: 'judgeResult',
104
+ inputSchema: z.string(),
105
+ fn: async name => {
106
+ ...
107
+ return new EvaluationStringResult({
108
+ value: 'good',
109
+ confidence: .95
110
+ });
111
+ }
112
+ } )
113
+ ```
114
+
115
+ Its usage is the same as steps:
116
+ `workflow.js`
117
+ ```js
118
+ import { workflow, z } from '@output.ai/workflow';
119
+ import { judgeResult } from './evaluators.js';
62
120
 
63
- workflows can call webhooks that will stop their execution until an answer is given back.
121
+ export default workflow( {
122
+ name: 'guessMyProfession',
123
+ inputSchema: z.object( {
124
+ name: z.string()
125
+ } ),
126
+ outputSchema: z.object( {
127
+ result: z.string()
128
+ } ),
129
+ fn: async input => {
130
+ const judgment = await judgeResult( input.name );
131
+ return { result: judgement.value };
132
+ }
133
+ })
134
+ ```
135
+
136
+ ## Webhooks
137
+
138
+ Workflows can call webhooks that will stop their execution until an answer is given back.
64
139
 
65
140
  ```js
66
141
  import { workflow, createWebhook } from '@output.ai/workflow';
@@ -107,6 +182,21 @@ POST http://locahost:3001/workflow/feedback
107
182
  }
108
183
  ```
109
184
 
185
+ ## Options
186
+
187
+ All core interface functions: workflow, step, evaluator have similar signature, with the following options:
188
+ - name: The function name, used to call it internally and identify it in the trace files, must be a code friendly string;
189
+ - description: Human description of the workflow/step, used for the catalog;
190
+ - inputSchema: a zod object indicating the type of the argument received by the `fn` function. It is validated. Omit if it doesn't have input arguments;
191
+ - outputSchema: a zod object indicating the type of that the `fn` function returns. It is validated. Omit if it is void. Evaluators do not have this option, since they must always return an EvaluationResult object;
192
+ - fn: The actual implementation of the workflow/step, including all its logic.
193
+ - options: Advanced options that will overwrite Temporal's ActivityOptions when calling activities.
194
+
195
+ If used on `workflow()` it will apply for all activities. If used on `step()` or `evaluator()` it will apply only to that underlying activity. If changed in both places, the end value will be a merge between the initial values, workflow values and the step values.
196
+
197
+ Order of precedence
198
+ `step options > workflow options > default options`
199
+
110
200
  ## Developing
111
201
 
112
202
  To develop workflows you need the code, which will be called the worker, the API and the engine (Temporal).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/core",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "The core module of the output framework",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,6 +11,10 @@
11
11
  "./tracing": {
12
12
  "types": "./src/tracing/index.d.ts",
13
13
  "import": "./src/tracing/index.js"
14
+ },
15
+ "./utils": {
16
+ "types": "./src/utils/index.d.ts",
17
+ "import": "./src/utils/index.js"
14
18
  }
15
19
  },
16
20
  "files": [
@@ -33,15 +37,17 @@
33
37
  "@temporalio/worker": "1.13.1",
34
38
  "@temporalio/workflow": "1.13.1",
35
39
  "redis": "5.8.3",
40
+ "stacktrace-parser": "0.1.11",
36
41
  "zod": "4.1.12"
37
42
  },
38
43
  "license": "UNLICENSED",
39
44
  "imports": {
40
45
  "#consts": "./src/consts.js",
41
46
  "#errors": "./src/errors.js",
42
- "#utils": "./src/utils.js",
47
+ "#utils": "./src/utils/index.js",
43
48
  "#tracing": "./src/tracing/internal_interface.js",
44
49
  "#async_storage": "./src/async_storage.js",
50
+ "#temporal_options": "./src/temporal_options.js",
45
51
  "#internal_activities": "./src/internal_activities/index.js"
46
52
  }
47
53
  }
package/src/consts.js CHANGED
@@ -1,6 +1,8 @@
1
1
  export const ACTIVITY_SEND_WEBHOOK = '__internal#sendWebhook';
2
2
  export const METADATA_ACCESS_SYMBOL = Symbol( '__metadata' );
3
+ export const SHARED_STEP_PREFIX = '__shared#';
3
4
  export const WORKFLOWS_INDEX_FILENAME = '__workflows_entrypoint.js';
5
+ export const ACTIVITY_OPTIONS_FILENAME = '__activity_options.js';
4
6
  export const WORKFLOW_CATALOG = '$catalog';
5
7
  export const ComponentType = {
6
8
  EVALUATOR: 'evaluator',
package/src/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  // Import Zod types for dual schema support
2
2
  import type { z } from 'zod';
3
+ import type { ActivityOptions } from '@temporalio/workflow';
3
4
 
4
5
  // Re-export Zod for consumers to use
5
6
  export { z } from 'zod';
@@ -19,6 +20,17 @@ export { z } from 'zod';
19
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
21
  type AnyZodSchema = z.ZodType<any, any, any>;
21
22
 
23
+ /**
24
+ * Activity retry options accepted by step/evaluator/workflow definitions.
25
+ * Uses Temporal's ActivityOptions['retry'] type.
26
+ */
27
+ export type Options = { retry?: ActivityOptions['retry'] };
28
+
29
+ /**
30
+ * @typedef {object} Options
31
+ * @property {import('@temporalio/workflow').ActivityOptions['retry']} [retry]
32
+ */
33
+
22
34
  /*
23
35
  ╭─────────╮
24
36
  │ S T E P │╮
@@ -43,6 +55,7 @@ type AnyZodSchema = z.ZodType<any, any, any>;
43
55
  * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
44
56
  * @param {z.ZodType} options.outputSchema - Zod schema for the fn output
45
57
  * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
58
+ * @param {Options} [options.options] - Activity retry options
46
59
  * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
47
60
  */
48
61
  export async function step<
@@ -54,6 +67,7 @@ export async function step<
54
67
  inputSchema: InputSchema;
55
68
  outputSchema: OutputSchema;
56
69
  fn: ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
70
+ options?: Options;
57
71
  } ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
58
72
 
59
73
  /**
@@ -64,6 +78,7 @@ export async function step<
64
78
  * @param {string} [options.description] - Description of the step
65
79
  * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
66
80
  * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<void>`
81
+ * @param {Options} [options.options] - Activity retry options
67
82
  * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<void>`
68
83
  */
69
84
  export async function step<
@@ -73,6 +88,7 @@ export async function step<
73
88
  description?: string;
74
89
  inputSchema: InputSchema;
75
90
  fn: ( input: z.infer<InputSchema> ) => Promise<void>;
91
+ options?: Options;
76
92
  } ): ( input: z.infer<InputSchema> ) => Promise<void>;
77
93
 
78
94
  /**
@@ -83,6 +99,7 @@ export async function step<
83
99
  * @param {string} [options.description] - Description of the step
84
100
  * @param {z.ZodType} options.outputSchema - Zod schema for the fn output
85
101
  * @param {function} options.fn - The function logic: `() => Promise<z.infer<OutputSchema>>`
102
+ * @param {Options} [options.options] - Activity retry options
86
103
  * @returns {function} Function with signature: `() => Promise<z.infer<OutputSchema>>`
87
104
  */
88
105
  export async function step<
@@ -92,6 +109,7 @@ export async function step<
92
109
  description?: string;
93
110
  outputSchema: OutputSchema;
94
111
  fn: () => Promise<z.infer<OutputSchema>>;
112
+ options?: Options;
95
113
  } ): () => Promise<z.infer<OutputSchema>>;
96
114
 
97
115
  /**
@@ -101,12 +119,14 @@ export async function step<
101
119
  * @param {string} options.name - Human-readable step name (only letters, numbers and "_")
102
120
  * @param {string} [options.description] - Description of the step
103
121
  * @param {function} options.fn - The function logic: `() => Promise<void>`
122
+ * @param {Options} [options.options] - Activity retry options
104
123
  * @returns {function} Function with signature: `() => Promise<void>`
105
124
  */
106
125
  export async function step( options: {
107
126
  name: string;
108
127
  description?: string;
109
128
  fn: () => Promise<void>;
129
+ options?: Options;
110
130
  } ): () => Promise<void>;
111
131
 
112
132
  /*
@@ -132,6 +152,7 @@ export async function step( options: {
132
152
  * @param {z.ZodType} options.inputSchema - Zod schema for workflow input
133
153
  * @param {z.ZodType} options.outputSchema - Zod schema for workflow output
134
154
  * @param {function} options.fn - Workflow logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
155
+ * @param {Options} [options.options] - Activity retry options
135
156
  * @returns {function} Callable workflow function: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
136
157
  */
137
158
  export function workflow<
@@ -142,7 +163,8 @@ export function workflow<
142
163
  description?: string,
143
164
  inputSchema: InputSchema,
144
165
  outputSchema: OutputSchema,
145
- fn: ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>
166
+ fn: ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>,
167
+ options?: Options
146
168
  } ): ( input: z.infer<InputSchema> ) => Promise<z.infer<OutputSchema>>;
147
169
 
148
170
  /**
@@ -153,6 +175,7 @@ export function workflow<
153
175
  * @param {string} [options.description] - Description of the workflow
154
176
  * @param {z.ZodType} options.inputSchema - Zod schema for workflow input
155
177
  * @param {function} options.fn - Workflow logic: `(input: z.infer<InputSchema>) => Promise<void>`
178
+ * @param {Options} [options.options] - Activity retry options
156
179
  * @returns {function} Callable workflow function: `(input: z.infer<InputSchema>) => Promise<void>`
157
180
  */
158
181
  export function workflow<
@@ -161,7 +184,8 @@ export function workflow<
161
184
  name: string,
162
185
  description?: string,
163
186
  inputSchema: InputSchema,
164
- fn: ( input: z.infer<InputSchema> ) => Promise<void>
187
+ fn: ( input: z.infer<InputSchema> ) => Promise<void>,
188
+ options?: Options
165
189
  } ): ( input: z.infer<InputSchema> ) => Promise<void>;
166
190
 
167
191
  /**
@@ -172,6 +196,7 @@ export function workflow<
172
196
  * @param {string} [options.description] - Description of the workflow
173
197
  * @param {z.ZodType} options.outputSchema - Zod schema for workflow output
174
198
  * @param {function} options.fn - Workflow logic: `() => Promise<z.infer<OutputSchema>>`
199
+ * @param {Options} [options.options] - Activity retry options
175
200
  * @returns {function} Callable workflow function: `() => Promise<z.infer<OutputSchema>>`
176
201
  */
177
202
  export function workflow<
@@ -180,7 +205,8 @@ export function workflow<
180
205
  name: string,
181
206
  description?: string,
182
207
  outputSchema: OutputSchema,
183
- fn: () => Promise<z.infer<OutputSchema>>
208
+ fn: () => Promise<z.infer<OutputSchema>>,
209
+ options?: Options
184
210
  } ): () => Promise<z.infer<OutputSchema>>;
185
211
 
186
212
  /**
@@ -190,12 +216,14 @@ export function workflow<
190
216
  * @param {string} options.name - Unique workflow name
191
217
  * @param {string} [options.description] - Description of the workflow
192
218
  * @param {function} options.fn - Workflow logic: `() => Promise<void>`
219
+ * @param {Options} [options.options] - Activity retry options
193
220
  * @returns {function} Callable workflow function: `() => Promise<void>`
194
221
  */
195
222
  export function workflow( options : {
196
223
  name: string,
197
224
  description?: string,
198
- fn: () => Promise<void>
225
+ fn: () => Promise<void>,
226
+ options?: Options
199
227
  } ): () => Promise<void>;
200
228
 
201
229
  /*
@@ -309,6 +337,7 @@ export class EvaluationBooleanResult extends EvaluationResult {
309
337
  * @param {string} [options.description] - Description of the evaluator
310
338
  * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
311
339
  * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
340
+ * @param {Options} [options.options] - Activity retry options
312
341
  * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
313
342
  */
314
343
  export async function evaluator<
@@ -319,6 +348,7 @@ export async function evaluator<
319
348
  description?: string;
320
349
  inputSchema: InputSchema;
321
350
  fn: ( input: z.infer<InputSchema> ) => Promise<Result>;
351
+ options?: Options;
322
352
  } ): ( input: z.infer<InputSchema> ) => Promise<Result>;
323
353
 
324
354
  /**
@@ -329,6 +359,7 @@ export async function evaluator<
329
359
  * @param {string} [options.description] - Description of the evaluator
330
360
  * @param {z.ZodType} options.inputSchema - Zod schema for the fn input
331
361
  * @param {function} options.fn - The function logic: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
362
+ * @param {Options} [options.options] - Activity retry options
332
363
  * @returns {function} Function with signature: `(input: z.infer<InputSchema>) => Promise<z.infer<OutputSchema>>`
333
364
  */
334
365
  export async function evaluator<
@@ -338,6 +369,7 @@ export async function evaluator<
338
369
  name: string;
339
370
  description?: string;
340
371
  fn: ( input: z.infer<InputSchema> ) => Promise<Result>;
372
+ options?: Options;
341
373
  } ): ( input: z.infer<InputSchema> ) => Promise<Result>;
342
374
 
343
375
  /*
@@ -1,6 +1,6 @@
1
- import { setMetadata } from './metadata.js';
2
1
  import { validateEvaluator } from './validations/static.js';
3
2
  import { validateWithSchema } from './validations/runtime.js';
3
+ import { setMetadata } from '#utils';
4
4
  import { ValidationError } from '#errors';
5
5
  import { ComponentType } from '#consts';
6
6
  import * as z from 'zod';
@@ -130,8 +130,8 @@ export class EvaluationNumberResult extends EvaluationResult {
130
130
  }
131
131
  };
132
132
 
133
- export function evaluator( { name, description, inputSchema, fn } ) {
134
- validateEvaluator( { name, description, inputSchema, fn } );
133
+ export function evaluator( { name, description, inputSchema, fn, options } ) {
134
+ validateEvaluator( { name, description, inputSchema, fn, options } );
135
135
 
136
136
  const wrapper = async input => {
137
137
  validateWithSchema( inputSchema, input, `Evaluator ${name} input` );
@@ -145,6 +145,6 @@ export function evaluator( { name, description, inputSchema, fn } ) {
145
145
  return output;
146
146
  };
147
147
 
148
- setMetadata( wrapper, { name, description, inputSchema, type: ComponentType.EVALUATOR } );
148
+ setMetadata( wrapper, { name, description, inputSchema, type: ComponentType.EVALUATOR, options } );
149
149
  return wrapper;
150
150
  };
@@ -1,10 +1,10 @@
1
- import { setMetadata } from './metadata.js';
1
+ import { setMetadata } from '#utils';
2
2
  import { validateStep } from './validations/static.js';
3
3
  import { validateWithSchema } from './validations/runtime.js';
4
4
  import { ComponentType } from '#consts';
5
5
 
6
- export function step( { name, description, inputSchema, outputSchema, fn } ) {
7
- validateStep( { name, description, inputSchema, outputSchema, fn } );
6
+ export function step( { name, description, inputSchema, outputSchema, fn, options } ) {
7
+ validateStep( { name, description, inputSchema, outputSchema, fn, options } );
8
8
 
9
9
  const wrapper = async input => {
10
10
  validateWithSchema( inputSchema, input, `Step ${name} input` );
@@ -16,6 +16,6 @@ export function step( { name, description, inputSchema, outputSchema, fn } ) {
16
16
  return output;
17
17
  };
18
18
 
19
- setMetadata( wrapper, { name, description, inputSchema, outputSchema, type: ComponentType.STEP } );
19
+ setMetadata( wrapper, { name, description, inputSchema, outputSchema, type: ComponentType.STEP, options } );
20
20
  return wrapper;
21
21
  };
@@ -17,12 +17,26 @@ const refineSchema = ( value, ctx ) => {
17
17
  } );
18
18
  };
19
19
 
20
- const stepAndWorkflowSchema = z.object( {
20
+ export const durationStringSchema = z.string().regex(
21
+ /^(\d+)(ms|s|m|h|d)$/,
22
+ 'Expected duration like "500ms", "10s", "5m", "2h", or "1d"'
23
+ );
24
+
25
+ const stepAndWorkflowSchema = z.strictObject( {
21
26
  name: z.string().regex( /^[a-z_][a-z0-9_]*$/i ),
22
27
  description: z.string().optional(),
23
28
  inputSchema: z.any().optional().superRefine( refineSchema ),
24
29
  outputSchema: z.any().optional().superRefine( refineSchema ),
25
- fn: z.function()
30
+ fn: z.function(),
31
+ options: z.strictObject( {
32
+ retry: z.strictObject( {
33
+ initialInterval: durationStringSchema.optional(),
34
+ backoffCoefficient: z.number().gte( 1 ).optional(),
35
+ maximumInterval: durationStringSchema.optional(),
36
+ maximumAttempts: z.number().gte( 1 ).int().optional(),
37
+ nonRetryableErrorTypes: z.array( z.string() ).optional()
38
+ } ).optional()
39
+ } ).optional()
26
40
  } );
27
41
 
28
42
  const evaluatorSchema = stepAndWorkflowSchema.omit( { outputSchema: true } );
@@ -65,6 +65,26 @@ describe( 'interface/validator', () => {
65
65
  const error = new StaticValidationError( '✖ Invalid input: expected function, received string\n → at fn' );
66
66
  expect( () => validateStep( { ...validArgs, fn: 'not-fn' } ) ).toThrow( error );
67
67
  } );
68
+
69
+ it( 'passes with options.retry (second-level options)', () => {
70
+ const args = {
71
+ ...validArgs,
72
+ options: {
73
+ retry: {
74
+ initialInterval: '1s',
75
+ backoffCoefficient: 2,
76
+ maximumInterval: '10s',
77
+ maximumAttempts: 3,
78
+ nonRetryableErrorTypes: [ 'SomeError' ]
79
+ }
80
+ }
81
+ };
82
+ expect( () => validateStep( args ) ).not.toThrow();
83
+ } );
84
+
85
+ it( 'rejects unknown top-level keys due to strictObject', () => {
86
+ expect( () => validateStep( { ...validArgs, extra: 123 } ) ).toThrow( StaticValidationError );
87
+ } );
68
88
  } );
69
89
 
70
90
  describe( 'validateWorkflow', () => {
@@ -1,27 +1,28 @@
1
1
  // THIS RUNS IN THE TEMPORAL'S SANDBOX ENVIRONMENT
2
2
  import { proxyActivities, inWorkflowContext, executeChild, workflowInfo } from '@temporalio/workflow';
3
- import { getInvocationDir } from './utils.js';
4
- import { setMetadata } from './metadata.js';
5
- import { FatalError, ValidationError } from '#errors';
6
3
  import { validateWorkflow } from './validations/static.js';
7
4
  import { validateWithSchema } from './validations/runtime.js';
5
+ import { SHARED_STEP_PREFIX } from '#consts';
6
+ import { mergeActivityOptions, resolveInvocationDir, setMetadata } from '#utils';
7
+ import { FatalError, ValidationError } from '#errors';
8
8
 
9
- const temporalActivityConfigs = {
10
- startToCloseTimeout: '20 minute',
9
+ const defaultActivityOptions = {
10
+ startToCloseTimeout: '20m',
11
11
  retry: {
12
12
  initialInterval: '10s',
13
13
  backoffCoefficient: 2.0,
14
- maximumInterval: '2 minutes',
14
+ maximumInterval: '2m',
15
15
  maximumAttempts: 3,
16
16
  nonRetryableErrorTypes: [ ValidationError.name, FatalError.name ]
17
17
  }
18
18
  };
19
19
 
20
- export function workflow( { name, description, inputSchema, outputSchema, fn } ) {
21
- validateWorkflow( { name, description, inputSchema, outputSchema, fn } );
22
- const workflowPath = getInvocationDir();
20
+ export function workflow( { name, description, inputSchema, outputSchema, fn, options } ) {
21
+ validateWorkflow( { name, description, inputSchema, outputSchema, fn, options } );
22
+ const workflowPath = resolveInvocationDir();
23
23
 
24
- const steps = proxyActivities( temporalActivityConfigs );
24
+ const activityOptions = mergeActivityOptions( defaultActivityOptions, options );
25
+ const steps = proxyActivities( activityOptions );
25
26
 
26
27
  const wrapper = async input => {
27
28
  validateWithSchema( inputSchema, input, `Workflow ${name} input` );
@@ -44,12 +45,16 @@ export function workflow( { name, description, inputSchema, outputSchema, fn } )
44
45
  startTime: startTime.getTime()
45
46
  };
46
47
 
47
- Object.assign( memo, { executionContext } );
48
+ Object.assign( memo, {
49
+ executionContext,
50
+ activityOptions: memo.activityOptions ?? activityOptions // Also preserve the original activity options
51
+ } );
48
52
 
49
- // binds the methods called in the code that Webpack loader will add, they will exposed via "this"
53
+ // binds the methods called in the code that Webpack loader will add, they will exposed via "this"
50
54
  const output = await fn.call( {
51
- invokeStep: async ( stepName, input ) => steps[`${workflowPath}#${stepName}`]( input ),
52
- invokeEvaluator: async ( evaluatorName, input ) => steps[`${workflowPath}#${evaluatorName}`]( input ),
55
+ invokeStep: async ( stepName, input, options ) => steps[`${workflowPath}#${stepName}`]( input, options ),
56
+ invokeSharedStep: async ( stepName, input, options ) => steps[`${SHARED_STEP_PREFIX}#${stepName}`]( input, options ),
57
+ invokeEvaluator: async ( evaluatorName, input, options ) => steps[`${workflowPath}#${evaluatorName}`]( input, options ),
53
58
 
54
59
  startWorkflow: async ( childName, input ) => {
55
60
  return executeChild( childName, { args: input ? [ input ] : [], memo: { executionContext, parentId: workflowId } } );
@@ -410,12 +410,12 @@ describe( 'Zod Schema Integration Tests', () => {
410
410
  inputSchema,
411
411
  fn: async input => {
412
412
  switch ( input.action ) {
413
- case 'create':
414
- return `Creating ${input.type}: ${input.name}`;
415
- case 'delete':
416
- return `Deleting item ${input.id}`;
417
- default:
418
- throw new Error( 'Unknown action' );
413
+ case 'create':
414
+ return `Creating ${input.type}: ${input.name}`;
415
+ case 'delete':
416
+ return `Deleting item ${input.id}`;
417
+ default:
418
+ throw new Error( 'Unknown action' );
419
419
  }
420
420
  }
421
421
  } );
@@ -1,5 +1,5 @@
1
1
  import { FatalError } from '#errors';
2
- import { setMetadata } from '../interface/metadata.js';
2
+ import { setMetadata } from '#utils';
3
3
  import { ComponentType } from '#consts';
4
4
 
5
5
  /**
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Return the directory of the file invoking the code that called this function
3
+ * Excludes `@output.ai/core`, node, and other internal paths
4
+ */
5
+ export function resolveInvocationDir(): string;
6
+
7
+ /**
8
+ * Node safe clone implementation that doesn't use global structuredClone()
9
+ * @param {object} v
10
+ * @returns {object}
11
+ */
12
+ export function clone( v: object ): object;
13
+
14
+ /**
15
+ * Throw given error
16
+ * @param {Error} e
17
+ * @throws {e}
18
+ */
19
+ export function throws( e: Error ): void;
20
+
21
+ /**
22
+ * Add metadata "values" property to a given object
23
+ * @param {object} target
24
+ * @param {object} values
25
+ * @returns
26
+ */
27
+ export function setMetadata( target: object, values: object ): void;
28
+
29
+ /**
30
+ * Merge two temporal activity options
31
+ */
32
+ export function mergeActivityOptions(
33
+ base?: import( '@temporalio/workflow' ).ActivityOptions,
34
+ ext?: import( '@temporalio/workflow' ).ActivityOptions
35
+ ): import( '@temporalio/workflow' ).ActivityOptions;
@@ -0,0 +1,2 @@
1
+ export { default as resolveInvocationDir } from './resolve_invocation_dir.js';
2
+ export * from './utils.js';
@@ -0,0 +1,27 @@
1
+ import * as stackTraceParser from 'stacktrace-parser';
2
+
3
+ // OS separator, but in a deterministic way, allowing this to work in Temporal's sandbox
4
+ // This avoids importing from node:path
5
+ const SEP = new Error().stack.includes( '/' ) ? '/' : '\\';
6
+ const ignorePaths = [
7
+ `${SEP}node_modules${SEP}`,
8
+ `${SEP}app${SEP}sdk${SEP}`,
9
+ `node:internal${SEP}`,
10
+ 'evalmachine.',
11
+ `webpack${SEP}bootstrap`
12
+ ];
13
+
14
+ /**
15
+ * Return the directory of the file invoking the code that called this function
16
+ * Excludes some internal paths and the sdk itself
17
+ */
18
+ export default () => {
19
+ const stack = new Error().stack;
20
+ const lines = stackTraceParser.parse( stack );
21
+
22
+ const frame = lines.find( l => !ignorePaths.some( p => l.file.includes( p ) ) );
23
+ if ( !frame ) {
24
+ throw new Error( `Invocation dir resolution via stack trace failed. Stack: ${stack}` );
25
+ }
26
+ return frame.file.replace( 'file://', '' ).split( SEP ).slice( 0, -1 ).join( SEP );
27
+ };