@codemation/core-nodes 0.1.1 → 0.3.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.
@@ -10,7 +10,14 @@ import type {
10
10
  ToolExecuteArgs,
11
11
  ZodSchemaAny,
12
12
  } from "@codemation/core";
13
- import { CoreTokens, inject, injectable, ItemValueResolver, NodeOutputNormalizer } from "@codemation/core";
13
+ import {
14
+ CoreTokens,
15
+ inject,
16
+ injectable,
17
+ ItemValueResolver,
18
+ NodeOutputNormalizer,
19
+ RunnableOutputBehaviorResolver,
20
+ } from "@codemation/core";
14
21
  import { z } from "zod";
15
22
 
16
23
  @injectable()
@@ -22,6 +29,8 @@ export class NodeBackedToolRuntime {
22
29
  private readonly itemValueResolver: ItemValueResolver,
23
30
  @inject(NodeOutputNormalizer)
24
31
  private readonly outputNormalizer: NodeOutputNormalizer,
32
+ @inject(RunnableOutputBehaviorResolver)
33
+ private readonly outputBehaviorResolver: RunnableOutputBehaviorResolver,
25
34
  ) {}
26
35
 
27
36
  async execute(
@@ -64,7 +73,7 @@ export class NodeBackedToolRuntime {
64
73
  if (this.isRunnableNode(resolvedNode)) {
65
74
  const runnable = resolvedNode;
66
75
  const runnableConfig = ctx.config;
67
- const carry = runnableConfig.lineageCarry ?? "emitOnly";
76
+ const behavior = this.outputBehaviorResolver.resolve(runnableConfig);
68
77
  const inputSchema = runnable.inputSchema ?? runnableConfig.inputSchema ?? z.unknown();
69
78
  const parsed = inputSchema.parse(nodeInput.json);
70
79
  const items = [nodeInput];
@@ -80,7 +89,7 @@ export class NodeBackedToolRuntime {
80
89
  return this.outputNormalizer.normalizeExecuteResult({
81
90
  baseItem: nodeInput,
82
91
  raw,
83
- carry,
92
+ behavior,
84
93
  });
85
94
  }
86
95
  throw new Error(`Node-backed tool expected a runnable node instance for "${ctx.config.name ?? ctx.nodeId}".`);
@@ -1,5 +1,7 @@
1
1
  export { AgentMessageFactory } from "./AgentMessageFactory";
2
2
  export { AgentOutputFactory } from "./AgentOutputFactory";
3
+ export { AgentStructuredOutputRepairPromptFactory } from "./AgentStructuredOutputRepairPromptFactory";
4
+ export { AgentStructuredOutputRunner } from "./AgentStructuredOutputRunner";
3
5
  export { AgentToolCallPortMap } from "./AgentToolCallPortMapFactory";
4
6
  export { AIAgent } from "./AIAgentConfig";
5
7
  export { AIAgentExecutionHelpersFactory } from "./AIAgentExecutionHelpersFactory";
@@ -2,6 +2,11 @@ import type { Item, NodeExecutionContext, RunnableNodeConfig, TypeToken } from "
2
2
 
3
3
  import { MapDataNode } from "./MapDataNode";
4
4
 
5
+ export interface MapDataOptions {
6
+ readonly id?: string;
7
+ readonly keepBinaries?: boolean;
8
+ }
9
+
5
10
  export class MapData<TInputJson = unknown, TOutputJson = unknown> implements RunnableNodeConfig<
6
11
  TInputJson,
7
12
  TOutputJson
@@ -11,14 +16,22 @@ export class MapData<TInputJson = unknown, TOutputJson = unknown> implements Run
11
16
  readonly execution = { hint: "local" } as const;
12
17
  /** Zero mapped items should still allow downstream nodes to run. */
13
18
  readonly continueWhenEmptyOutput = true as const;
19
+ readonly keepBinaries: boolean;
20
+
14
21
  constructor(
15
22
  public readonly name: string,
16
23
  public readonly map: (
17
24
  item: Item<TInputJson>,
18
25
  ctx: NodeExecutionContext<MapData<TInputJson, TOutputJson>>,
19
26
  ) => TOutputJson,
20
- public readonly id?: string,
21
- ) {}
27
+ private readonly options: MapDataOptions = {},
28
+ ) {
29
+ this.keepBinaries = options.keepBinaries ?? true;
30
+ }
31
+
32
+ get id(): string | undefined {
33
+ return this.options.id;
34
+ }
22
35
  }
23
36
 
24
37
  export { MapDataNode } from "./MapDataNode";
@@ -14,7 +14,6 @@ export class Switch<TInputJson = unknown> implements RunnableNodeConfig<TInputJs
14
14
  readonly type: TypeToken<unknown> = SwitchNode;
15
15
  readonly execution = { hint: "local" } as const;
16
16
  readonly icon = "lucide:git-branch-plus" as const;
17
- readonly lineageCarry = "carryThrough" as const;
18
17
  readonly declaredOutputPorts: ReadonlyArray<string>;
19
18
 
20
19
  constructor(
@@ -10,22 +10,18 @@ export class WorkflowAgentNodeFactory {
10
10
  ): AIAgent<TCurrentJson, TOutputSchema extends z.ZodTypeAny ? z.output<TOutputSchema> : Record<string, unknown>> {
11
11
  const options = typeof nameOrOptions === "string" ? optionsOrUndefined! : nameOrOptions;
12
12
  const name = typeof nameOrOptions === "string" ? nameOrOptions : "AI agent";
13
- const prompt = options.prompt;
14
- const messages = [
15
- {
16
- role: "user" as const,
17
- content:
18
- typeof prompt === "function" ? ({ item }: { item: { json: TCurrentJson } }) => prompt(item.json) : prompt,
19
- },
20
- ] as const;
13
+ const outputSchema = options.outputSchema as
14
+ | z.ZodType<TOutputSchema extends z.ZodTypeAny ? z.output<TOutputSchema> : Record<string, unknown>>
15
+ | undefined;
21
16
  return new AIAgent<
22
17
  TCurrentJson,
23
18
  TOutputSchema extends z.ZodTypeAny ? z.output<TOutputSchema> : Record<string, unknown>
24
19
  >({
25
20
  name,
26
- messages,
21
+ messages: options.messages,
27
22
  chatModel: WorkflowChatModelFactory.create(options.model),
28
23
  tools: options.tools,
24
+ outputSchema,
29
25
  id: options.id,
30
26
  retryPolicy: options.retryPolicy,
31
27
  guardrails: options.guardrails,
@@ -1,10 +1,16 @@
1
- import type { AgentGuardrailConfig, ChatModelConfig, RunnableNodeConfig, ToolConfig } from "@codemation/core";
1
+ import type {
2
+ AgentGuardrailConfig,
3
+ AgentMessageConfig,
4
+ ChatModelConfig,
5
+ RunnableNodeConfig,
6
+ ToolConfig,
7
+ } from "@codemation/core";
2
8
  import { z } from "zod";
3
9
 
4
- export type WorkflowAgentPrompt<TCurrentJson> = string | ((item: TCurrentJson) => string);
10
+ export type WorkflowAgentMessages<TCurrentJson> = AgentMessageConfig<TCurrentJson>;
5
11
 
6
12
  export interface WorkflowAgentOptions<TCurrentJson, TOutputSchema extends z.ZodTypeAny | undefined = undefined> {
7
- readonly prompt: WorkflowAgentPrompt<TCurrentJson>;
13
+ readonly messages: WorkflowAgentMessages<TCurrentJson>;
8
14
  readonly model: string | ChatModelConfig;
9
15
  readonly tools?: ReadonlyArray<ToolConfig>;
10
16
  readonly outputSchema?: TOutputSchema;
@@ -10,6 +10,7 @@ import type {
10
10
  import { z } from "zod";
11
11
  import { Aggregate } from "../nodes/aggregate";
12
12
  import { Filter } from "../nodes/filter";
13
+ import type { MapDataOptions } from "../nodes/mapData";
13
14
  import { MapData } from "../nodes/mapData";
14
15
  import { Split } from "../nodes/split";
15
16
  import { Wait } from "../nodes/wait";
@@ -18,6 +19,11 @@ import { WorkflowAgentNodeFactory } from "./WorkflowAgentNodeFactory.types";
18
19
  import { WorkflowDefinedNodeResolver } from "./WorkflowDefinedNodeResolver.types";
19
20
  import { WorkflowDurationParser } from "./WorkflowDurationParser.types";
20
21
 
22
+ type WorkflowMapCallback<TCurrentJson, TNextJson> = (
23
+ item: Item<TCurrentJson>,
24
+ ctx: NodeExecutionContext<MapData<TCurrentJson, TNextJson>>,
25
+ ) => TNextJson;
26
+
21
27
  export class WorkflowBranchBuilder<TCurrentJson> {
22
28
  constructor(private readonly steps: ReadonlyArray<AnyRunnableNodeConfig> = []) {}
23
29
 
@@ -27,22 +33,20 @@ export class WorkflowBranchBuilder<TCurrentJson> {
27
33
  return new WorkflowBranchBuilder<RunnableNodeOutputJson<TConfig>>([...this.steps, config]);
28
34
  }
29
35
 
30
- map<TNextJson>(mapper: (item: TCurrentJson) => TNextJson): WorkflowBranchBuilder<TNextJson>;
36
+ map<TNextJson>(mapper: WorkflowMapCallback<TCurrentJson, TNextJson>): WorkflowBranchBuilder<TNextJson>;
31
37
  map<TNextJson>(
32
38
  name: string,
33
- mapper: (item: TCurrentJson) => TNextJson,
34
- id?: string,
39
+ mapper: WorkflowMapCallback<TCurrentJson, TNextJson>,
40
+ options?: MapDataOptions,
35
41
  ): WorkflowBranchBuilder<TNextJson>;
36
42
  map<TNextJson>(
37
- nameOrMapper: string | ((item: TCurrentJson) => TNextJson),
38
- mapperOrUndefined?: (item: TCurrentJson) => TNextJson,
39
- id?: string,
43
+ nameOrMapper: string | WorkflowMapCallback<TCurrentJson, TNextJson>,
44
+ mapperOrUndefined?: WorkflowMapCallback<TCurrentJson, TNextJson>,
45
+ options?: MapDataOptions,
40
46
  ): WorkflowBranchBuilder<TNextJson> {
41
47
  const name = typeof nameOrMapper === "string" ? nameOrMapper : "Map data";
42
48
  const mapper = typeof nameOrMapper === "string" ? mapperOrUndefined! : nameOrMapper;
43
- return this.then(
44
- new MapData<TCurrentJson, TNextJson>(name, (item) => mapper(item.json as TCurrentJson), id),
45
- ) as WorkflowBranchBuilder<TNextJson>;
49
+ return this.then(new MapData<TCurrentJson, TNextJson>(name, mapper, options)) as WorkflowBranchBuilder<TNextJson>;
46
50
  }
47
51
 
48
52
  wait(duration: number | string): WorkflowBranchBuilder<TCurrentJson>;
@@ -12,6 +12,7 @@ import { z } from "zod";
12
12
  import { Aggregate } from "../nodes/aggregate";
13
13
  import { Filter } from "../nodes/filter";
14
14
  import { If } from "../nodes/if";
15
+ import type { MapDataOptions } from "../nodes/mapData";
15
16
  import { MapData } from "../nodes/mapData";
16
17
  import { Merge, type MergeMode } from "../nodes/merge";
17
18
  import { Split } from "../nodes/split";
@@ -28,6 +29,18 @@ type BranchCallback<TCurrentJson, TNextJson> = (
28
29
  ) => WorkflowBranchBuilder<TNextJson>;
29
30
  type RouteBranchCallback<TCurrentJson, TNextJson> = (branch: WorkflowChain<TCurrentJson>) => WorkflowChain<TNextJson>;
30
31
  type BranchOutputMatch<TLeft, TRight> = [TLeft] extends [TRight] ? ([TRight] extends [TLeft] ? true : false) : false;
32
+ type WorkflowMapCallback<TCurrentJson, TNextJson> = (
33
+ item: Item<TCurrentJson>,
34
+ ctx: NodeExecutionContext<MapData<TCurrentJson, TNextJson>>,
35
+ ) => TNextJson;
36
+ type WorkflowIfPredicate<TCurrentJson> = (
37
+ item: Item<TCurrentJson>,
38
+ ctx: NodeExecutionContext<If<TCurrentJson>>,
39
+ ) => boolean;
40
+ type WorkflowSwitchCaseKeyResolver<TCurrentJson> = (
41
+ item: Item<TCurrentJson>,
42
+ ctx: NodeExecutionContext<Switch<TCurrentJson>>,
43
+ ) => string | Promise<string>;
31
44
 
32
45
  export class WorkflowChain<TCurrentJson> {
33
46
  constructor(private readonly chain: ChainCursor<TCurrentJson>) {}
@@ -38,18 +51,20 @@ export class WorkflowChain<TCurrentJson> {
38
51
  return new WorkflowChain(this.chain.then(config));
39
52
  }
40
53
 
41
- map<TNextJson>(mapper: (item: TCurrentJson) => TNextJson): WorkflowChain<TNextJson>;
42
- map<TNextJson>(name: string, mapper: (item: TCurrentJson) => TNextJson, id?: string): WorkflowChain<TNextJson>;
54
+ map<TNextJson>(mapper: WorkflowMapCallback<TCurrentJson, TNextJson>): WorkflowChain<TNextJson>;
43
55
  map<TNextJson>(
44
- nameOrMapper: string | ((item: TCurrentJson) => TNextJson),
45
- mapperOrUndefined?: (item: TCurrentJson) => TNextJson,
46
- id?: string,
56
+ name: string,
57
+ mapper: WorkflowMapCallback<TCurrentJson, TNextJson>,
58
+ options?: MapDataOptions,
59
+ ): WorkflowChain<TNextJson>;
60
+ map<TNextJson>(
61
+ nameOrMapper: string | WorkflowMapCallback<TCurrentJson, TNextJson>,
62
+ mapperOrUndefined?: WorkflowMapCallback<TCurrentJson, TNextJson>,
63
+ options?: MapDataOptions,
47
64
  ): WorkflowChain<TNextJson> {
48
65
  const name = typeof nameOrMapper === "string" ? nameOrMapper : "Map data";
49
66
  const mapper = typeof nameOrMapper === "string" ? mapperOrUndefined! : nameOrMapper;
50
- return this.then(
51
- new MapData<TCurrentJson, TNextJson>(name, (item) => mapper(item.json as TCurrentJson), id),
52
- ) as WorkflowChain<TNextJson>;
67
+ return this.then(new MapData<TCurrentJson, TNextJson>(name, mapper, options)) as WorkflowChain<TNextJson>;
53
68
  }
54
69
 
55
70
  wait(duration: number | string): WorkflowChain<TCurrentJson>;
@@ -185,7 +200,7 @@ export class WorkflowChain<TCurrentJson> {
185
200
  }
186
201
 
187
202
  if<TBranchJson>(
188
- predicate: (item: TCurrentJson) => boolean,
203
+ predicate: WorkflowIfPredicate<TCurrentJson>,
189
204
  branches: Readonly<{
190
205
  true?: BranchCallback<TCurrentJson, TBranchJson>;
191
206
  false?: BranchCallback<TCurrentJson, TBranchJson>;
@@ -193,16 +208,16 @@ export class WorkflowChain<TCurrentJson> {
193
208
  ): WorkflowChain<TBranchJson>;
194
209
  if<TBranchJson>(
195
210
  name: string,
196
- predicate: (item: TCurrentJson) => boolean,
211
+ predicate: WorkflowIfPredicate<TCurrentJson>,
197
212
  branches: Readonly<{
198
213
  true?: BranchCallback<TCurrentJson, TBranchJson>;
199
214
  false?: BranchCallback<TCurrentJson, TBranchJson>;
200
215
  }>,
201
216
  ): WorkflowChain<TBranchJson>;
202
217
  if<TTrueJson, TFalseJson>(
203
- nameOrPredicate: string | ((item: TCurrentJson) => boolean),
218
+ nameOrPredicate: string | WorkflowIfPredicate<TCurrentJson>,
204
219
  predicateOrBranches:
205
- | ((item: TCurrentJson) => boolean)
220
+ | WorkflowIfPredicate<TCurrentJson>
206
221
  | Readonly<{ true?: BranchCallback<TCurrentJson, TTrueJson>; false?: BranchCallback<TCurrentJson, TFalseJson> }>,
207
222
  branchesOrUndefined?: Readonly<{
208
223
  true?: BranchCallback<TCurrentJson, TTrueJson>;
@@ -211,12 +226,14 @@ export class WorkflowChain<TCurrentJson> {
211
226
  ): WorkflowChain<BranchOutputMatch<TTrueJson, TFalseJson> extends true ? TTrueJson : never> {
212
227
  const name = typeof nameOrPredicate === "string" ? nameOrPredicate : "If";
213
228
  const predicate =
214
- typeof nameOrPredicate === "string" ? (predicateOrBranches as (item: TCurrentJson) => boolean) : nameOrPredicate;
229
+ typeof nameOrPredicate === "string"
230
+ ? (predicateOrBranches as WorkflowIfPredicate<TCurrentJson>)
231
+ : nameOrPredicate;
215
232
  const branches = (typeof nameOrPredicate === "string" ? branchesOrUndefined : predicateOrBranches) as Readonly<{
216
233
  true?: BranchCallback<TCurrentJson, TTrueJson>;
217
234
  false?: BranchCallback<TCurrentJson, TFalseJson>;
218
235
  }>;
219
- const cursor = this.chain.then(new If<TCurrentJson>(name, (item) => predicate(item.json as TCurrentJson)));
236
+ const cursor = this.chain.then(new If<TCurrentJson>(name, (item, _index, _items, ctx) => predicate(item, ctx)));
220
237
  const trueSteps = branches.true?.(new WorkflowBranchBuilder<TCurrentJson>()).getSteps();
221
238
  const falseSteps = branches.false?.(new WorkflowBranchBuilder<TCurrentJson>()).getSteps();
222
239
  return new WorkflowChain(
@@ -253,7 +270,7 @@ export class WorkflowChain<TCurrentJson> {
253
270
  cfg: Readonly<{
254
271
  cases: readonly string[];
255
272
  defaultCase: string;
256
- resolveCaseKey: (item: TCurrentJson) => string | Promise<string>;
273
+ resolveCaseKey: WorkflowSwitchCaseKeyResolver<TCurrentJson>;
257
274
  branches: Readonly<Record<string, RouteBranchCallback<TCurrentJson, TBranchJson> | undefined>>;
258
275
  }>,
259
276
  ): WorkflowChain<TBranchJson>;
@@ -262,7 +279,7 @@ export class WorkflowChain<TCurrentJson> {
262
279
  cfg: Readonly<{
263
280
  cases: readonly string[];
264
281
  defaultCase: string;
265
- resolveCaseKey: (item: TCurrentJson) => string | Promise<string>;
282
+ resolveCaseKey: WorkflowSwitchCaseKeyResolver<TCurrentJson>;
266
283
  branches: Readonly<Record<string, RouteBranchCallback<TCurrentJson, TBranchJson> | undefined>>;
267
284
  }>,
268
285
  id?: string,
@@ -273,13 +290,13 @@ export class WorkflowChain<TCurrentJson> {
273
290
  | Readonly<{
274
291
  cases: readonly string[];
275
292
  defaultCase: string;
276
- resolveCaseKey: (item: TCurrentJson) => string | Promise<string>;
293
+ resolveCaseKey: WorkflowSwitchCaseKeyResolver<TCurrentJson>;
277
294
  branches: Readonly<Record<string, RouteBranchCallback<TCurrentJson, TBranchJson> | undefined>>;
278
295
  }>,
279
296
  cfgOrUndefined?: Readonly<{
280
297
  cases: readonly string[];
281
298
  defaultCase: string;
282
- resolveCaseKey: (item: TCurrentJson) => string | Promise<string>;
299
+ resolveCaseKey: WorkflowSwitchCaseKeyResolver<TCurrentJson>;
283
300
  branches: Readonly<Record<string, RouteBranchCallback<TCurrentJson, TBranchJson> | undefined>>;
284
301
  }>,
285
302
  id?: string,
@@ -292,7 +309,7 @@ export class WorkflowChain<TCurrentJson> {
292
309
  {
293
310
  cases: cfg.cases,
294
311
  defaultCase: cfg.defaultCase,
295
- resolveCaseKey: (item) => cfg.resolveCaseKey(item.json as TCurrentJson),
312
+ resolveCaseKey: (item, _index, _items, ctx) => cfg.resolveCaseKey(item, ctx),
296
313
  },
297
314
  id,
298
315
  ),
@@ -1,4 +1,4 @@
1
- export type { WorkflowAgentOptions } from "./workflowAuthoring/WorkflowAuthoringOptions.types";
1
+ export type { WorkflowAgentMessages, WorkflowAgentOptions } from "./workflowAuthoring/WorkflowAuthoringOptions.types";
2
2
  export { WorkflowAuthoringBuilder } from "./workflowAuthoring/WorkflowAuthoringBuilder.types";
3
3
  export { WorkflowBranchBuilder } from "./workflowAuthoring/WorkflowBranchBuilder.types";
4
4
  export { WorkflowChain } from "./workflowAuthoring/WorkflowChain.types";