@codemation/core-nodes 0.0.24 → 0.1.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/CHANGELOG.md +27 -0
- package/dist/index.cjs +267 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +250 -138
- package/dist/index.d.ts +250 -138
- package/dist/index.js +256 -137
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
- package/src/index.ts +1 -0
- package/src/nodes/AIAgentConfig.ts +5 -10
- package/src/nodes/AIAgentNode.ts +7 -11
- package/src/nodes/AggregateNode.ts +6 -7
- package/src/nodes/CallbackNode.ts +18 -6
- package/src/nodes/CallbackNodeFactory.ts +31 -4
- package/src/nodes/CallbackResultNormalizerFactory.ts +7 -3
- package/src/nodes/ConnectionCredentialNode.ts +4 -4
- package/src/nodes/FilterNode.ts +6 -10
- package/src/nodes/HttpRequestNodeFactory.ts +4 -8
- package/src/nodes/IfNode.ts +15 -27
- package/src/nodes/MapDataNode.ts +3 -9
- package/src/nodes/NoOpNode.ts +4 -4
- package/src/nodes/NodeBackedToolRuntime.ts +40 -8
- package/src/nodes/SplitNode.ts +5 -15
- package/src/nodes/SubWorkflowNode.ts +43 -45
- package/src/nodes/SwitchNode.ts +29 -0
- package/src/nodes/WaitNode.ts +11 -9
- package/src/nodes/if.ts +1 -0
- package/src/nodes/mergeExecutionUtils.types.ts +31 -5
- package/src/nodes/switch.ts +33 -0
- package/src/register.types.ts +2 -0
- package/src/workflowAuthoring/WorkflowBranchBuilder.types.ts +108 -2
- package/src/workflowAuthoring/WorkflowChain.types.ts +207 -2
- package/src/workflowBuilder.types.ts +1 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/core-nodes",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -31,11 +31,12 @@
|
|
|
31
31
|
"@langchain/core": "^1.1.31",
|
|
32
32
|
"@langchain/openai": "^1.2.12",
|
|
33
33
|
"lucide-react": "^0.577.0",
|
|
34
|
-
"@codemation/core": "0.
|
|
34
|
+
"@codemation/core": "0.4.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.3.5",
|
|
38
38
|
"eslint": "^10.0.3",
|
|
39
|
+
"reflect-metadata": "^0.2.2",
|
|
39
40
|
"tsdown": "^0.15.5",
|
|
40
41
|
"tsx": "^4.21.0",
|
|
41
42
|
"typescript": "^5.9.3",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"zod": "^4.3.6"
|
|
44
45
|
},
|
|
45
46
|
"scripts": {
|
|
47
|
+
"changeset:verify": "pnpm --workspace-root run changeset:verify",
|
|
46
48
|
"dev": "tsdown --watch",
|
|
47
49
|
"build": "tsdown",
|
|
48
50
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./nodes/httpRequest";
|
|
|
9
9
|
export * from "./nodes/aggregate";
|
|
10
10
|
export * from "./nodes/filter";
|
|
11
11
|
export * from "./nodes/if";
|
|
12
|
+
export * from "./nodes/switch";
|
|
12
13
|
export * from "./nodes/split";
|
|
13
14
|
export * from "./nodes/ManualTriggerFactory";
|
|
14
15
|
export * from "./nodes/mapData";
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
type AgentMessageConfig,
|
|
5
5
|
type AgentNodeConfig,
|
|
6
6
|
type ChatModelConfig,
|
|
7
|
-
type ItemInputMapper,
|
|
8
7
|
type RetryPolicySpec,
|
|
9
8
|
type RunnableNodeConfig,
|
|
10
9
|
type ToolConfig,
|
|
@@ -14,7 +13,7 @@ import type { ZodType } from "zod";
|
|
|
14
13
|
|
|
15
14
|
import { AIAgentNode } from "./AIAgentNode";
|
|
16
15
|
|
|
17
|
-
export interface AIAgentOptions<TInputJson = unknown, _TOutputJson = unknown
|
|
16
|
+
export interface AIAgentOptions<TInputJson = unknown, _TOutputJson = unknown> {
|
|
18
17
|
readonly name: string;
|
|
19
18
|
readonly messages: AgentMessageConfig<TInputJson>;
|
|
20
19
|
readonly chatModel: ChatModelConfig;
|
|
@@ -22,18 +21,16 @@ export interface AIAgentOptions<TInputJson = unknown, _TOutputJson = unknown, TW
|
|
|
22
21
|
readonly id?: string;
|
|
23
22
|
readonly retryPolicy?: RetryPolicySpec;
|
|
24
23
|
readonly guardrails?: AgentGuardrailConfig;
|
|
25
|
-
/** Engine applies with {@link RunnableNodeConfig.inputSchema} before {@link AIAgentNode.
|
|
24
|
+
/** Engine applies with {@link RunnableNodeConfig.inputSchema} before {@link AIAgentNode.execute}. */
|
|
26
25
|
readonly inputSchema?: ZodType<TInputJson>;
|
|
27
|
-
/** Per-item mapper before validation; use with {@link inputSchema} so persisted run inputs show the prompt payload. */
|
|
28
|
-
readonly mapInput?: ItemInputMapper<TWireJson, TInputJson>;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
29
|
* AI agent: credential bindings are keyed to connection-owned LLM/tool node ids (ConnectionNodeIdFactory),
|
|
33
30
|
* not to the agent workflow node id.
|
|
34
31
|
*/
|
|
35
|
-
export class AIAgent<TInputJson = unknown, TOutputJson = unknown
|
|
36
|
-
implements RunnableNodeConfig<TInputJson, TOutputJson
|
|
32
|
+
export class AIAgent<TInputJson = unknown, TOutputJson = unknown>
|
|
33
|
+
implements RunnableNodeConfig<TInputJson, TOutputJson>, AgentNodeConfig<TInputJson, TOutputJson>
|
|
37
34
|
{
|
|
38
35
|
readonly kind = "node" as const;
|
|
39
36
|
readonly type: TypeToken<unknown> = AIAgentNode;
|
|
@@ -47,9 +44,8 @@ export class AIAgent<TInputJson = unknown, TOutputJson = unknown, TWireJson = TI
|
|
|
47
44
|
readonly retryPolicy: RetryPolicySpec;
|
|
48
45
|
readonly guardrails?: AgentGuardrailConfig;
|
|
49
46
|
readonly inputSchema?: ZodType<TInputJson>;
|
|
50
|
-
readonly mapInput?: ItemInputMapper<TWireJson, TInputJson>;
|
|
51
47
|
|
|
52
|
-
constructor(options: AIAgentOptions<TInputJson, TOutputJson
|
|
48
|
+
constructor(options: AIAgentOptions<TInputJson, TOutputJson>) {
|
|
53
49
|
this.name = options.name;
|
|
54
50
|
this.messages = options.messages;
|
|
55
51
|
this.chatModel = options.chatModel;
|
|
@@ -58,6 +54,5 @@ export class AIAgent<TInputJson = unknown, TOutputJson = unknown, TWireJson = TI
|
|
|
58
54
|
this.retryPolicy = options.retryPolicy ?? RetryPolicy.defaultForAiAgent;
|
|
59
55
|
this.guardrails = options.guardrails;
|
|
60
56
|
this.inputSchema = options.inputSchema;
|
|
61
|
-
this.mapInput = options.mapInput;
|
|
62
57
|
}
|
|
63
58
|
}
|
package/src/nodes/AIAgentNode.ts
CHANGED
|
@@ -4,12 +4,13 @@ import type {
|
|
|
4
4
|
ChatModelConfig,
|
|
5
5
|
ChatModelFactory,
|
|
6
6
|
Item,
|
|
7
|
-
ItemNode,
|
|
8
7
|
Items,
|
|
9
8
|
JsonValue,
|
|
10
9
|
LangChainChatModelLike,
|
|
11
10
|
NodeExecutionContext,
|
|
12
11
|
NodeInputsByPort,
|
|
12
|
+
RunnableNode,
|
|
13
|
+
RunnableNodeExecuteArgs,
|
|
13
14
|
Tool,
|
|
14
15
|
ToolConfig,
|
|
15
16
|
ZodSchemaAny,
|
|
@@ -59,12 +60,13 @@ interface PreparedAgentExecution {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
@node({ packageName: "@codemation/core-nodes" })
|
|
62
|
-
export class AIAgentNode implements
|
|
63
|
+
export class AIAgentNode implements RunnableNode<AIAgent<any, any>> {
|
|
63
64
|
kind = "node" as const;
|
|
64
65
|
outputPorts = ["main"] as const;
|
|
65
66
|
/**
|
|
66
|
-
* Engine
|
|
67
|
-
*
|
|
67
|
+
* Engine validates {@link RunnableNodeConfig.inputSchema} (Zod) on {@code item.json} before enqueue, then resolves
|
|
68
|
+
* per-item **`itemValue`** leaves on config before {@link #execute}. Prefer modeling prompts as
|
|
69
|
+
* {@code { messages: [{ role, content }, ...] }} (on input or config) so persisted inputs are visible in the UI.
|
|
68
70
|
*/
|
|
69
71
|
readonly inputSchema = z.unknown();
|
|
70
72
|
|
|
@@ -89,13 +91,7 @@ export class AIAgentNode implements ItemNode<AIAgent<any, any>, unknown, unknown
|
|
|
89
91
|
this.executionHelpers.createConnectionCredentialExecutionContextFactory(credentialSessions);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
|
-
async
|
|
93
|
-
input: unknown;
|
|
94
|
-
item: Item;
|
|
95
|
-
itemIndex: number;
|
|
96
|
-
items: Items;
|
|
97
|
-
ctx: NodeExecutionContext<AIAgent<any, any>>;
|
|
98
|
-
}): Promise<unknown> {
|
|
94
|
+
async execute(args: RunnableNodeExecuteArgs<AIAgent<any, any>>): Promise<unknown> {
|
|
99
95
|
const prepared = await this.getOrPrepareExecution(args.ctx);
|
|
100
96
|
const itemWithMappedJson = { ...args.item, json: args.input };
|
|
101
97
|
const resultItem = await this.runAgentForItem(prepared, itemWithMappedJson, args.itemIndex, args.items);
|
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
|
|
5
5
|
import type { Aggregate } from "./aggregate";
|
|
6
6
|
|
|
7
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
8
|
-
export class AggregateNode implements
|
|
8
|
+
export class AggregateNode implements RunnableNode<Aggregate<any, any>> {
|
|
9
9
|
kind = "node" as const;
|
|
10
10
|
outputPorts = ["main"] as const;
|
|
11
11
|
|
|
12
|
-
async execute(
|
|
13
|
-
if (items.length
|
|
14
|
-
return
|
|
12
|
+
async execute(args: RunnableNodeExecuteArgs<Aggregate<any, any>>): Promise<unknown> {
|
|
13
|
+
if (args.itemIndex !== args.items.length - 1) {
|
|
14
|
+
return [];
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
return { main: [{ json }] };
|
|
16
|
+
return Promise.resolve(args.ctx.config.aggregate(args.items, args.ctx));
|
|
18
17
|
}
|
|
19
18
|
}
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
3
2
|
import { node } from "@codemation/core";
|
|
4
3
|
|
|
5
4
|
import { Callback } from "./CallbackNodeFactory";
|
|
6
5
|
import { CallbackResultNormalizer } from "./CallbackResultNormalizerFactory";
|
|
7
6
|
|
|
8
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
9
|
-
export class CallbackNode implements
|
|
8
|
+
export class CallbackNode implements RunnableNode<Callback<any, any>> {
|
|
10
9
|
kind = "node" as const;
|
|
11
10
|
outputPorts = ["main"] as const;
|
|
12
11
|
|
|
13
|
-
async execute(
|
|
14
|
-
const
|
|
15
|
-
|
|
12
|
+
async execute(args: RunnableNodeExecuteArgs<Callback<any, any>>): Promise<unknown> {
|
|
13
|
+
const items = args.items ?? [];
|
|
14
|
+
const ctx = args.ctx;
|
|
15
|
+
const config = ctx.config;
|
|
16
|
+
if (config == null) {
|
|
17
|
+
throw new Error("CallbackNode: missing ctx.config (engine should always pass runnable config)");
|
|
18
|
+
}
|
|
19
|
+
if (items.length === 0) {
|
|
20
|
+
const result = await config.callback(items, ctx);
|
|
21
|
+
return CallbackResultNormalizer.toPortsEmission(result, items);
|
|
22
|
+
}
|
|
23
|
+
if (args.itemIndex !== items.length - 1) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const result = await config.callback(items, ctx);
|
|
27
|
+
return CallbackResultNormalizer.toPortsEmission(result, items);
|
|
16
28
|
}
|
|
17
29
|
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Items,
|
|
3
|
+
NodeExecutionContext,
|
|
4
|
+
NodeErrorHandlerSpec,
|
|
5
|
+
PortsEmission,
|
|
6
|
+
RetryPolicySpec,
|
|
7
|
+
RunnableNodeConfig,
|
|
8
|
+
TypeToken,
|
|
9
|
+
} from "@codemation/core";
|
|
2
10
|
|
|
3
11
|
import { CallbackNode } from "./CallbackNode";
|
|
4
12
|
|
|
@@ -9,7 +17,14 @@ export type CallbackHandler<
|
|
|
9
17
|
> = (
|
|
10
18
|
items: Items<TInputJson>,
|
|
11
19
|
ctx: NodeExecutionContext<TConfig>,
|
|
12
|
-
) => Promise<Items<TOutputJson> | void> | Items<TOutputJson> | void;
|
|
20
|
+
) => Promise<Items<TOutputJson> | PortsEmission | void> | Items<TOutputJson> | PortsEmission | void;
|
|
21
|
+
|
|
22
|
+
export type CallbackOptions = Readonly<{
|
|
23
|
+
id?: string;
|
|
24
|
+
retryPolicy?: RetryPolicySpec;
|
|
25
|
+
nodeErrorHandler?: NodeErrorHandlerSpec;
|
|
26
|
+
declaredOutputPorts?: ReadonlyArray<string>;
|
|
27
|
+
}>;
|
|
13
28
|
|
|
14
29
|
export class Callback<TInputJson = unknown, TOutputJson = TInputJson> implements RunnableNodeConfig<
|
|
15
30
|
TInputJson,
|
|
@@ -19,6 +34,11 @@ export class Callback<TInputJson = unknown, TOutputJson = TInputJson> implements
|
|
|
19
34
|
readonly type: TypeToken<unknown> = CallbackNode;
|
|
20
35
|
readonly execution = { hint: "local" } as const;
|
|
21
36
|
readonly icon = "lucide:braces" as const;
|
|
37
|
+
readonly emptyBatchExecution = "runOnce" as const;
|
|
38
|
+
readonly id?: string;
|
|
39
|
+
readonly retryPolicy?: RetryPolicySpec;
|
|
40
|
+
readonly nodeErrorHandler?: NodeErrorHandlerSpec;
|
|
41
|
+
readonly declaredOutputPorts?: ReadonlyArray<string>;
|
|
22
42
|
|
|
23
43
|
constructor(
|
|
24
44
|
public readonly name: string = "Callback",
|
|
@@ -26,8 +46,15 @@ export class Callback<TInputJson = unknown, TOutputJson = TInputJson> implements
|
|
|
26
46
|
TInputJson,
|
|
27
47
|
TOutputJson
|
|
28
48
|
>,
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
idOrOptions?: string | CallbackOptions,
|
|
50
|
+
options?: CallbackOptions,
|
|
51
|
+
) {
|
|
52
|
+
const resolvedOptions = typeof idOrOptions === "string" ? { ...options, id: idOrOptions } : idOrOptions;
|
|
53
|
+
this.id = resolvedOptions?.id;
|
|
54
|
+
this.retryPolicy = resolvedOptions?.retryPolicy;
|
|
55
|
+
this.nodeErrorHandler = resolvedOptions?.nodeErrorHandler;
|
|
56
|
+
this.declaredOutputPorts = resolvedOptions?.declaredOutputPorts;
|
|
57
|
+
}
|
|
31
58
|
|
|
32
59
|
private static defaultCallback<TItemJson>(items: Items<TItemJson>): Items<TItemJson> {
|
|
33
60
|
return items;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import type { Items,
|
|
1
|
+
import type { Items, PortsEmission } from "@codemation/core";
|
|
2
|
+
import { emitPorts, isPortsEmission } from "@codemation/core";
|
|
2
3
|
|
|
3
4
|
export class CallbackResultNormalizer {
|
|
4
|
-
static
|
|
5
|
-
|
|
5
|
+
static toPortsEmission(result: Items | PortsEmission | void, items: Items): PortsEmission {
|
|
6
|
+
if (isPortsEmission(result)) {
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
return emitPorts({ main: result ?? items });
|
|
6
10
|
}
|
|
7
11
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
import { node } from "@codemation/core";
|
|
3
3
|
|
|
4
4
|
import type { ConnectionCredentialNodeConfig } from "./ConnectionCredentialNodeConfig";
|
|
@@ -8,11 +8,11 @@ import type { ConnectionCredentialNodeConfig } from "./ConnectionCredentialNodeC
|
|
|
8
8
|
* The engine does not schedule these; they exist for credentials, tokens, and UI identity.
|
|
9
9
|
*/
|
|
10
10
|
@node({ packageName: "@codemation/core-nodes" })
|
|
11
|
-
export class ConnectionCredentialNode implements
|
|
11
|
+
export class ConnectionCredentialNode implements RunnableNode<ConnectionCredentialNodeConfig> {
|
|
12
12
|
kind = "node" as const;
|
|
13
13
|
outputPorts = ["main"] as const;
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
return
|
|
15
|
+
execute(_args: RunnableNodeExecuteArgs<ConnectionCredentialNodeConfig>): unknown {
|
|
16
|
+
return [];
|
|
17
17
|
}
|
|
18
18
|
}
|
package/src/nodes/FilterNode.ts
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
import type { Item,
|
|
1
|
+
import type { Item, RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
|
|
5
5
|
import type { Filter } from "./filter";
|
|
6
6
|
|
|
7
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
8
|
-
export class FilterNode implements
|
|
8
|
+
export class FilterNode implements RunnableNode<Filter<any>> {
|
|
9
9
|
kind = "node" as const;
|
|
10
10
|
outputPorts = ["main"] as const;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const item = items[i] as Item;
|
|
16
|
-
if (ctx.config.predicate(item as Item, i, items as Items, ctx)) {
|
|
17
|
-
out.push(item);
|
|
18
|
-
}
|
|
12
|
+
execute(args: RunnableNodeExecuteArgs<Filter<any>>): unknown {
|
|
13
|
+
if (args.ctx.config.predicate(args.item as Item, args.itemIndex, args.items, args.ctx)) {
|
|
14
|
+
return args.item;
|
|
19
15
|
}
|
|
20
|
-
return
|
|
16
|
+
return [];
|
|
21
17
|
}
|
|
22
18
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Item,
|
|
1
|
+
import type { Item, NodeExecutionContext, RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
|
|
@@ -6,16 +6,12 @@ import type { HttpRequestDownloadMode } from "./httpRequest";
|
|
|
6
6
|
import { HttpRequest } from "./httpRequest";
|
|
7
7
|
|
|
8
8
|
@node({ packageName: "@codemation/core-nodes" })
|
|
9
|
-
export class HttpRequestNode implements
|
|
9
|
+
export class HttpRequestNode implements RunnableNode<HttpRequest<any, any>> {
|
|
10
10
|
readonly kind = "node" as const;
|
|
11
11
|
readonly outputPorts = ["main"] as const;
|
|
12
12
|
|
|
13
|
-
async execute(
|
|
14
|
-
|
|
15
|
-
for (const item of items) {
|
|
16
|
-
output.push(await this.executeItem(item, ctx));
|
|
17
|
-
}
|
|
18
|
-
return { main: output };
|
|
13
|
+
async execute(args: RunnableNodeExecuteArgs<HttpRequest<any, any>>): Promise<unknown> {
|
|
14
|
+
return await this.executeItem(args.item, args.ctx);
|
|
19
15
|
}
|
|
20
16
|
|
|
21
17
|
private async executeItem(item: Item, ctx: NodeExecutionContext<HttpRequest<any, any>>): Promise<Item> {
|
package/src/nodes/IfNode.ts
CHANGED
|
@@ -1,35 +1,23 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
import { node } from "@codemation/core";
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
|
+
import { emitPorts, node } from "@codemation/core";
|
|
4
3
|
|
|
5
4
|
import { If } from "./if";
|
|
5
|
+
import { tagItemForRouterFanIn } from "./mergeExecutionUtils.types";
|
|
6
6
|
|
|
7
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
8
|
-
export class IfNode implements
|
|
8
|
+
export class IfNode implements RunnableNode<If<any>> {
|
|
9
9
|
kind = "node" as const;
|
|
10
|
-
outputPorts = ["true", "false"] as const;
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
: ({} as Record<string, unknown>);
|
|
24
|
-
const originIndex = typeof cmBase.originIndex === "number" ? (cmBase.originIndex as number) : i;
|
|
25
|
-
const tagged: Item = {
|
|
26
|
-
...item,
|
|
27
|
-
meta: { ...metaBase, _cm: { ...cmBase, originIndex } },
|
|
28
|
-
paired: [{ nodeId: ctx.nodeId, output: "$in", itemIndex: originIndex }, ...(item.paired ?? [])],
|
|
29
|
-
};
|
|
30
|
-
const ok = ctx.config.predicate(item, i, items, ctx);
|
|
31
|
-
(ok ? t : f).push(tagged);
|
|
32
|
-
}
|
|
33
|
-
return { true: t, false: f };
|
|
11
|
+
execute(args: RunnableNodeExecuteArgs<If<any>>): unknown {
|
|
12
|
+
const tagged = tagItemForRouterFanIn({
|
|
13
|
+
item: args.item,
|
|
14
|
+
itemIndex: args.itemIndex,
|
|
15
|
+
nodeId: args.ctx.nodeId,
|
|
16
|
+
});
|
|
17
|
+
const ok = args.ctx.config.predicate(args.item, args.itemIndex, args.items, args.ctx);
|
|
18
|
+
return emitPorts({
|
|
19
|
+
true: ok ? [tagged] : [],
|
|
20
|
+
false: ok ? [] : [tagged],
|
|
21
|
+
});
|
|
34
22
|
}
|
|
35
23
|
}
|
package/src/nodes/MapDataNode.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
import { z } from "zod";
|
|
@@ -6,18 +6,12 @@ import { z } from "zod";
|
|
|
6
6
|
import { MapData } from "./mapData";
|
|
7
7
|
|
|
8
8
|
@node({ packageName: "@codemation/core-nodes" })
|
|
9
|
-
export class MapDataNode implements
|
|
9
|
+
export class MapDataNode implements RunnableNode<MapData<any, any>> {
|
|
10
10
|
kind = "node" as const;
|
|
11
11
|
outputPorts = ["main"] as const;
|
|
12
12
|
readonly inputSchema = z.unknown();
|
|
13
13
|
|
|
14
|
-
async
|
|
15
|
-
input: unknown;
|
|
16
|
-
item: Item;
|
|
17
|
-
itemIndex: number;
|
|
18
|
-
items: Items;
|
|
19
|
-
ctx: NodeExecutionContext<MapData<any, any>>;
|
|
20
|
-
}): Promise<unknown> {
|
|
14
|
+
async execute(args: RunnableNodeExecuteArgs<MapData<any, any>>): Promise<unknown> {
|
|
21
15
|
return args.ctx.config.map(args.item, args.ctx);
|
|
22
16
|
}
|
|
23
17
|
}
|
package/src/nodes/NoOpNode.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
|
|
5
5
|
import { NoOp } from "./noOp";
|
|
6
6
|
|
|
7
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
8
|
-
export class NoOpNode implements
|
|
8
|
+
export class NoOpNode implements RunnableNode<NoOp<any>> {
|
|
9
9
|
kind = "node" as const;
|
|
10
10
|
outputPorts = ["main"] as const;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
return
|
|
12
|
+
execute(args: RunnableNodeExecuteArgs<NoOp<any>>): unknown {
|
|
13
|
+
return args.item;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
MultiInputNode,
|
|
3
|
-
Node,
|
|
4
3
|
NodeExecutionContext,
|
|
5
4
|
NodeOutputs,
|
|
6
5
|
NodeResolver,
|
|
6
|
+
RunnableNode,
|
|
7
|
+
RunnableNodeConfig,
|
|
8
|
+
RunnableNodeExecuteArgs,
|
|
7
9
|
NodeBackedToolConfig,
|
|
8
10
|
ToolExecuteArgs,
|
|
9
11
|
ZodSchemaAny,
|
|
10
12
|
} from "@codemation/core";
|
|
11
|
-
import { CoreTokens, inject, injectable } from "@codemation/core";
|
|
13
|
+
import { CoreTokens, inject, injectable, ItemValueResolver, NodeOutputNormalizer } from "@codemation/core";
|
|
14
|
+
import { z } from "zod";
|
|
12
15
|
|
|
13
16
|
@injectable()
|
|
14
17
|
export class NodeBackedToolRuntime {
|
|
15
18
|
constructor(
|
|
16
19
|
@inject(CoreTokens.NodeResolver)
|
|
17
20
|
private readonly nodeResolver: NodeResolver,
|
|
21
|
+
@inject(ItemValueResolver)
|
|
22
|
+
private readonly itemValueResolver: ItemValueResolver,
|
|
23
|
+
@inject(NodeOutputNormalizer)
|
|
24
|
+
private readonly outputNormalizer: NodeOutputNormalizer,
|
|
18
25
|
) {}
|
|
19
26
|
|
|
20
27
|
async execute(
|
|
@@ -32,7 +39,7 @@ export class NodeBackedToolRuntime {
|
|
|
32
39
|
const nodeCtx = {
|
|
33
40
|
...args.ctx,
|
|
34
41
|
config: config.node,
|
|
35
|
-
} as NodeExecutionContext<
|
|
42
|
+
} as NodeExecutionContext<RunnableNodeConfig>;
|
|
36
43
|
const resolvedNode = this.nodeResolver.resolve(config.node.type);
|
|
37
44
|
const outputs = await this.executeResolvedNode(resolvedNode, nodeInput, nodeCtx);
|
|
38
45
|
return config.toToolOutput({
|
|
@@ -49,19 +56,44 @@ export class NodeBackedToolRuntime {
|
|
|
49
56
|
private async executeResolvedNode(
|
|
50
57
|
resolvedNode: unknown,
|
|
51
58
|
nodeInput: ToolExecuteArgs["item"],
|
|
52
|
-
ctx: NodeExecutionContext<
|
|
59
|
+
ctx: NodeExecutionContext<RunnableNodeConfig>,
|
|
53
60
|
): Promise<NodeOutputs> {
|
|
54
61
|
if (this.isMultiInputNode(resolvedNode)) {
|
|
55
62
|
return await resolvedNode.executeMulti({ in: [nodeInput] }, ctx);
|
|
56
63
|
}
|
|
57
|
-
if (this.
|
|
58
|
-
|
|
64
|
+
if (this.isRunnableNode(resolvedNode)) {
|
|
65
|
+
const runnable = resolvedNode;
|
|
66
|
+
const runnableConfig = ctx.config;
|
|
67
|
+
const carry = runnableConfig.lineageCarry ?? "emitOnly";
|
|
68
|
+
const inputSchema = runnable.inputSchema ?? runnableConfig.inputSchema ?? z.unknown();
|
|
69
|
+
const parsed = inputSchema.parse(nodeInput.json);
|
|
70
|
+
const items = [nodeInput];
|
|
71
|
+
const resolvedCtx = await this.itemValueResolver.resolveConfigForItem(ctx, nodeInput, 0, items);
|
|
72
|
+
const execArgs: RunnableNodeExecuteArgs = {
|
|
73
|
+
input: parsed,
|
|
74
|
+
item: nodeInput,
|
|
75
|
+
itemIndex: 0,
|
|
76
|
+
items,
|
|
77
|
+
ctx: resolvedCtx,
|
|
78
|
+
};
|
|
79
|
+
const raw = await Promise.resolve(runnable.execute(execArgs));
|
|
80
|
+
return this.outputNormalizer.normalizeExecuteResult({
|
|
81
|
+
baseItem: nodeInput,
|
|
82
|
+
raw,
|
|
83
|
+
carry,
|
|
84
|
+
});
|
|
59
85
|
}
|
|
60
86
|
throw new Error(`Node-backed tool expected a runnable node instance for "${ctx.config.name ?? ctx.nodeId}".`);
|
|
61
87
|
}
|
|
62
88
|
|
|
63
|
-
private
|
|
64
|
-
return
|
|
89
|
+
private isRunnableNode(value: unknown): value is RunnableNode {
|
|
90
|
+
return (
|
|
91
|
+
typeof value === "object" &&
|
|
92
|
+
value !== null &&
|
|
93
|
+
(value as { kind?: string }).kind === "node" &&
|
|
94
|
+
typeof (value as { execute?: unknown }).execute === "function" &&
|
|
95
|
+
typeof (value as { executeMulti?: unknown }).executeMulti !== "function"
|
|
96
|
+
);
|
|
65
97
|
}
|
|
66
98
|
|
|
67
99
|
private isMultiInputNode(value: unknown): value is MultiInputNode<any> {
|
package/src/nodes/SplitNode.ts
CHANGED
|
@@ -1,26 +1,16 @@
|
|
|
1
|
-
import type { Item,
|
|
1
|
+
import type { Item, RunnableNode, RunnableNodeExecuteArgs } from "@codemation/core";
|
|
2
2
|
|
|
3
3
|
import { node } from "@codemation/core";
|
|
4
4
|
|
|
5
5
|
import type { Split } from "./split";
|
|
6
6
|
|
|
7
7
|
@node({ packageName: "@codemation/core-nodes" })
|
|
8
|
-
export class SplitNode implements
|
|
8
|
+
export class SplitNode implements RunnableNode<Split<any, any>> {
|
|
9
9
|
kind = "node" as const;
|
|
10
10
|
outputPorts = ["main"] as const;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
const item = items[i] as Item;
|
|
16
|
-
const elements = ctx.config.getElements(item, ctx);
|
|
17
|
-
for (let j = 0; j < elements.length; j++) {
|
|
18
|
-
out.push({
|
|
19
|
-
...item,
|
|
20
|
-
json: elements[j],
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return { main: out };
|
|
12
|
+
execute(args: RunnableNodeExecuteArgs<Split<any, any>>): unknown {
|
|
13
|
+
const elements = args.ctx.config.getElements(args.item as Item, args.ctx);
|
|
14
|
+
return elements;
|
|
25
15
|
}
|
|
26
16
|
}
|