@codemation/core-nodes 0.0.13 → 0.0.15

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/dist/index.cjs CHANGED
@@ -96,12 +96,18 @@ const openAiChatModelPresets = new OpenAiChatModelPresets();
96
96
  //#endregion
97
97
  //#region src/nodes/AgentMessageFactory.ts
98
98
  var AgentMessageFactory = class {
99
+ static createPromptMessages(messages) {
100
+ return messages.map((message) => this.createPromptMessage(message));
101
+ }
99
102
  static createSystemPrompt(systemMessage) {
100
103
  return new __langchain_core_messages.SystemMessage(systemMessage);
101
104
  }
102
105
  static createUserPrompt(prompt) {
103
106
  return new __langchain_core_messages.HumanMessage(prompt);
104
107
  }
108
+ static createAssistantPrompt(prompt) {
109
+ return new __langchain_core_messages.AIMessage(prompt);
110
+ }
105
111
  static createToolMessage(toolCallId, content) {
106
112
  return new __langchain_core_messages.ToolMessage({
107
113
  tool_call_id: toolCallId,
@@ -133,6 +139,11 @@ var AgentMessageFactory = class {
133
139
  static isRecord(value) {
134
140
  return typeof value === "object" && value !== null;
135
141
  }
142
+ static createPromptMessage(message) {
143
+ if (message.role === "system") return this.createSystemPrompt(message.content);
144
+ if (message.role === "assistant") return this.createAssistantPrompt(message.content);
145
+ return this.createUserPrompt(message.content);
146
+ }
136
147
  };
137
148
 
138
149
  //#endregion
@@ -188,12 +199,31 @@ var ConnectionCredentialExecutionContextFactory = class {
188
199
  };
189
200
 
190
201
  //#endregion
191
- //#region src/nodes/aiAgentSupport.types.ts
192
- var AgentItemPortMap = class {
193
- static fromItem(item) {
194
- return { in: [item] };
202
+ //#region src/nodes/AIAgentExecutionHelpersFactory.ts
203
+ let AIAgentExecutionHelpersFactory = class AIAgentExecutionHelpersFactory$1 {
204
+ createConnectionCredentialExecutionContextFactory(credentialSessions) {
205
+ return new ConnectionCredentialExecutionContextFactory(credentialSessions);
206
+ }
207
+ createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items) {
208
+ return new __langchain_core_tools.DynamicStructuredTool({
209
+ name: entry.config.name,
210
+ description: entry.config.description ?? entry.runtime.defaultDescription,
211
+ schema: entry.runtime.inputSchema,
212
+ func: async (input) => {
213
+ const result = await entry.runtime.execute({
214
+ config: entry.config,
215
+ input,
216
+ ctx: toolCredentialContext,
217
+ item,
218
+ itemIndex,
219
+ items
220
+ });
221
+ return JSON.stringify(result);
222
+ }
223
+ });
195
224
  }
196
225
  };
226
+ AIAgentExecutionHelpersFactory = __decorate([(0, __codemation_core.injectable)()], AIAgentExecutionHelpersFactory);
197
227
 
198
228
  //#endregion
199
229
  //#region \0@oxc-project+runtime@0.95.0/helpers/decorateMetadata.js
@@ -210,55 +240,164 @@ function __decorateParam(paramIndex, decorator) {
210
240
  }
211
241
 
212
242
  //#endregion
213
- //#region src/nodes/AIAgentNodeFactory.ts
243
+ //#region src/nodes/NodeBackedToolRuntime.ts
244
+ let NodeBackedToolRuntime = class NodeBackedToolRuntime$1 {
245
+ constructor(nodeResolver) {
246
+ this.nodeResolver = nodeResolver;
247
+ }
248
+ async execute(config, args) {
249
+ const nodeInput = config.toNodeItem({
250
+ input: args.input,
251
+ item: args.item,
252
+ itemIndex: args.itemIndex,
253
+ items: args.items,
254
+ ctx: args.ctx,
255
+ node: config.node
256
+ });
257
+ const nodeCtx = {
258
+ ...args.ctx,
259
+ config: config.node
260
+ };
261
+ const resolvedNode = this.nodeResolver.resolve(config.node.type);
262
+ const outputs = await this.executeResolvedNode(resolvedNode, nodeInput, nodeCtx);
263
+ return config.toToolOutput({
264
+ input: args.input,
265
+ item: args.item,
266
+ itemIndex: args.itemIndex,
267
+ items: args.items,
268
+ ctx: args.ctx,
269
+ node: config.node,
270
+ outputs
271
+ });
272
+ }
273
+ async executeResolvedNode(resolvedNode, nodeInput, ctx) {
274
+ if (this.isMultiInputNode(resolvedNode)) return await resolvedNode.executeMulti({ in: [nodeInput] }, ctx);
275
+ if (this.isNode(resolvedNode)) return await resolvedNode.execute([nodeInput], ctx);
276
+ throw new Error(`Node-backed tool expected a runnable node instance for "${ctx.config.name ?? ctx.nodeId}".`);
277
+ }
278
+ isNode(value) {
279
+ return typeof value === "object" && value !== null && "execute" in value;
280
+ }
281
+ isMultiInputNode(value) {
282
+ return typeof value === "object" && value !== null && "executeMulti" in value;
283
+ }
284
+ };
285
+ NodeBackedToolRuntime = __decorate([
286
+ (0, __codemation_core.injectable)(),
287
+ __decorateParam(0, (0, __codemation_core.inject)(__codemation_core.CoreTokens.NodeResolver)),
288
+ __decorateMetadata("design:paramtypes", [Object])
289
+ ], NodeBackedToolRuntime);
290
+
291
+ //#endregion
292
+ //#region src/nodes/aiAgentSupport.types.ts
293
+ var AgentItemPortMap = class {
294
+ static fromItem(item) {
295
+ return { in: [item] };
296
+ }
297
+ };
298
+
299
+ //#endregion
300
+ //#region src/nodes/AIAgentNode.ts
301
+ var _ref, _ref2;
214
302
  let AIAgentNode = class AIAgentNode$1 {
215
303
  kind = "node";
216
304
  outputPorts = ["main"];
217
305
  connectionCredentialExecutionContextFactory;
218
- constructor(nodeResolver, credentialSessions) {
306
+ constructor(nodeResolver, credentialSessions, nodeBackedToolRuntime, executionHelpers) {
219
307
  this.nodeResolver = nodeResolver;
220
- this.connectionCredentialExecutionContextFactory = new ConnectionCredentialExecutionContextFactory(credentialSessions);
308
+ this.nodeBackedToolRuntime = nodeBackedToolRuntime;
309
+ this.executionHelpers = executionHelpers;
310
+ this.connectionCredentialExecutionContextFactory = this.executionHelpers.createConnectionCredentialExecutionContextFactory(credentialSessions);
221
311
  }
222
312
  async execute(items, ctx) {
313
+ const prepared = await this.prepareExecution(ctx);
314
+ const out = [];
315
+ for (let i = 0; i < items.length; i++) out.push(await this.runAgentForItem(prepared, items[i], i, items));
316
+ return { main: out };
317
+ }
318
+ /**
319
+ * Resolves the chat model and tools once, then returns shared state for every item in the batch.
320
+ */
321
+ async prepareExecution(ctx) {
223
322
  const chatModelFactory = this.nodeResolver.resolve(ctx.config.chatModel.type);
224
323
  const languageModelCredentialContext = this.connectionCredentialExecutionContextFactory.forConnectionNode(ctx, {
225
324
  connectionNodeId: __codemation_core.ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId),
226
325
  getCredentialRequirements: () => ctx.config.chatModel.getCredentialRequirements?.() ?? []
227
326
  });
228
- const model = await Promise.resolve(chatModelFactory.create({
229
- config: ctx.config.chatModel,
230
- ctx: languageModelCredentialContext
231
- }));
232
- const resolvedTools = this.resolveTools(ctx.config.tools ?? []);
233
- const out = [];
234
- for (let i = 0; i < items.length; i++) {
235
- const item = items[i];
236
- const prompt = ctx.config.userMessageFormatter(item, i, items, ctx);
237
- const itemInputsByPort = AgentItemPortMap.fromItem(item);
238
- const itemScopedTools = this.createItemScopedTools(resolvedTools, ctx, item, i, items);
239
- const firstResponse = await this.invokeModel(itemScopedTools.length > 0 && model.bindTools ? model.bindTools(itemScopedTools.map((entry) => entry.langChainTool)) : model, __codemation_core.ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId), [AgentMessageFactory.createSystemPrompt(ctx.config.systemMessage), AgentMessageFactory.createUserPrompt(prompt)], ctx, itemInputsByPort);
240
- const toolCalls = AgentMessageFactory.extractToolCalls(firstResponse);
241
- if (toolCalls.length === 0) {
242
- out.push(AgentOutputFactory.replaceJson(item, AgentOutputFactory.fromAgentContent(AgentMessageFactory.extractContent(firstResponse))));
243
- continue;
327
+ return {
328
+ ctx,
329
+ model: await Promise.resolve(chatModelFactory.create({
330
+ config: ctx.config.chatModel,
331
+ ctx: languageModelCredentialContext
332
+ })),
333
+ resolvedTools: this.resolveTools(ctx.config.tools ?? []),
334
+ guardrails: this.resolveGuardrails(ctx.config.guardrails),
335
+ languageModelConnectionNodeId: __codemation_core.ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId)
336
+ };
337
+ }
338
+ /**
339
+ * One item: build prompts, optionally bind tools, run the multi-turn loop, map the final model message to workflow JSON.
340
+ */
341
+ async runAgentForItem(prepared, item, itemIndex, items) {
342
+ const { ctx } = prepared;
343
+ const itemInputsByPort = AgentItemPortMap.fromItem(item);
344
+ const itemScopedTools = this.createItemScopedTools(prepared.resolvedTools, ctx, item, itemIndex, items);
345
+ const conversation = [...this.createPromptMessages(item, itemIndex, items, ctx)];
346
+ const modelWithTools = this.bindToolsToModel(prepared.model, itemScopedTools);
347
+ const finalResponse = await this.runTurnLoopUntilFinalAnswer({
348
+ prepared,
349
+ itemInputsByPort,
350
+ itemScopedTools,
351
+ conversation,
352
+ modelWithTools
353
+ });
354
+ return this.buildOutputItem(item, finalResponse);
355
+ }
356
+ /**
357
+ * Repeatedly invokes the model until it returns without tool calls, or guardrails end the loop.
358
+ */
359
+ async runTurnLoopUntilFinalAnswer(args) {
360
+ const { prepared, itemInputsByPort, itemScopedTools, conversation, modelWithTools } = args;
361
+ const { ctx, guardrails, languageModelConnectionNodeId } = prepared;
362
+ let finalResponse;
363
+ for (let turn = 1; turn <= guardrails.maxTurns; turn++) {
364
+ const response = await this.invokeModel(modelWithTools, languageModelConnectionNodeId, conversation, ctx, itemInputsByPort, guardrails.modelInvocationOptions);
365
+ finalResponse = response;
366
+ const toolCalls = AgentMessageFactory.extractToolCalls(response);
367
+ if (toolCalls.length === 0) break;
368
+ if (this.cannotExecuteAnotherToolRound(turn, guardrails)) {
369
+ this.finishOrThrowWhenTurnCapHitWithToolCalls(ctx, guardrails);
370
+ break;
244
371
  }
245
372
  const plannedToolCalls = this.planToolCalls(itemScopedTools, toolCalls, ctx.nodeId);
246
373
  await this.markQueuedTools(plannedToolCalls, ctx);
247
374
  const executedToolCalls = await this.executeToolCalls(plannedToolCalls, ctx);
248
- const finalResponse = await this.invokeModel(itemScopedTools.length > 0 && model.bindTools ? model.bindTools(itemScopedTools.map((entry) => entry.langChainTool)) : model, __codemation_core.ConnectionNodeIdFactory.languageModelConnectionNodeId(ctx.nodeId), [
249
- AgentMessageFactory.createSystemPrompt(ctx.config.systemMessage),
250
- AgentMessageFactory.createUserPrompt(prompt),
251
- firstResponse,
252
- ...executedToolCalls.map((toolCall) => AgentMessageFactory.createToolMessage(toolCall.toolCallId, toolCall.serialized))
253
- ], ctx, itemInputsByPort);
254
- out.push(AgentOutputFactory.replaceJson(item, AgentOutputFactory.fromAgentContent(AgentMessageFactory.extractContent(finalResponse))));
375
+ this.appendAssistantAndToolMessages(conversation, response, executedToolCalls);
255
376
  }
256
- return { main: out };
377
+ if (!finalResponse) throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" did not produce a model response.`);
378
+ return finalResponse;
379
+ }
380
+ cannotExecuteAnotherToolRound(turn, guardrails) {
381
+ return turn >= guardrails.maxTurns;
382
+ }
383
+ finishOrThrowWhenTurnCapHitWithToolCalls(ctx, guardrails) {
384
+ if (guardrails.onTurnLimitReached === "respondWithLastMessage") return;
385
+ throw new Error(`AIAgent "${ctx.config.name ?? ctx.nodeId}" reached maxTurns=${guardrails.maxTurns} before producing a final response.`);
386
+ }
387
+ appendAssistantAndToolMessages(conversation, assistantMessage, executedToolCalls) {
388
+ conversation.push(assistantMessage, ...executedToolCalls.map((toolCall) => AgentMessageFactory.createToolMessage(toolCall.toolCallId, toolCall.serialized)));
389
+ }
390
+ buildOutputItem(item, finalResponse) {
391
+ return AgentOutputFactory.replaceJson(item, AgentOutputFactory.fromAgentContent(AgentMessageFactory.extractContent(finalResponse)));
392
+ }
393
+ bindToolsToModel(model, itemScopedTools) {
394
+ if (itemScopedTools.length === 0 || !model.bindTools) return model;
395
+ return model.bindTools(itemScopedTools.map((entry) => entry.langChainTool));
257
396
  }
258
397
  resolveTools(toolConfigs) {
259
398
  const resolvedTools = toolConfigs.map((config) => ({
260
399
  config,
261
- tool: this.nodeResolver.resolve(config.type)
400
+ runtime: this.resolveToolRuntime(config)
262
401
  }));
263
402
  const names = /* @__PURE__ */ new Set();
264
403
  for (const entry of resolvedTools) {
@@ -273,29 +412,14 @@ let AIAgentNode = class AIAgentNode$1 {
273
412
  connectionNodeId: __codemation_core.ConnectionNodeIdFactory.toolConnectionNodeId(ctx.nodeId, entry.config.name),
274
413
  getCredentialRequirements: () => entry.config.getCredentialRequirements?.() ?? []
275
414
  });
276
- const langChainTool = new __langchain_core_tools.DynamicStructuredTool({
277
- name: entry.config.name,
278
- description: entry.config.description ?? entry.tool.defaultDescription,
279
- schema: entry.tool.inputSchema,
280
- func: async (input) => {
281
- const result = await entry.tool.execute({
282
- config: entry.config,
283
- input,
284
- ctx: toolCredentialContext,
285
- item,
286
- itemIndex,
287
- items
288
- });
289
- return JSON.stringify(result);
290
- }
291
- });
415
+ const langChainTool = this.executionHelpers.createDynamicStructuredTool(entry, toolCredentialContext, item, itemIndex, items);
292
416
  return {
293
417
  config: entry.config,
294
418
  langChainTool
295
419
  };
296
420
  });
297
421
  }
298
- async invokeModel(model, nodeId, messages, ctx, inputsByPort) {
422
+ async invokeModel(model, nodeId, messages, ctx, inputsByPort, options) {
299
423
  await ctx.nodeState?.markQueued({
300
424
  nodeId,
301
425
  activationId: ctx.activationId,
@@ -307,7 +431,7 @@ let AIAgentNode = class AIAgentNode$1 {
307
431
  inputsByPort
308
432
  });
309
433
  try {
310
- const response = await model.invoke(messages);
434
+ const response = await model.invoke(messages, options);
311
435
  await ctx.nodeState?.markCompleted({
312
436
  nodeId,
313
437
  activationId: ctx.activationId,
@@ -441,12 +565,49 @@ let AIAgentNode = class AIAgentNode$1 {
441
565
  const json = JSON.stringify(value);
442
566
  return JSON.parse(json);
443
567
  }
568
+ createPromptMessages(item, itemIndex, items, ctx) {
569
+ return AgentMessageFactory.createPromptMessages(__codemation_core.AgentMessageConfigNormalizer.normalize(ctx.config, {
570
+ item,
571
+ itemIndex,
572
+ items,
573
+ ctx
574
+ }));
575
+ }
576
+ resolveToolRuntime(config) {
577
+ if (config instanceof __codemation_core.NodeBackedToolConfig) return {
578
+ defaultDescription: `Run workflow node "${config.node.name ?? config.name}" as an AI tool.`,
579
+ inputSchema: config.getInputSchema(),
580
+ execute: async (args) => await this.nodeBackedToolRuntime.execute(config, args)
581
+ };
582
+ const tool = this.nodeResolver.resolve(config.type);
583
+ return {
584
+ defaultDescription: tool.defaultDescription,
585
+ inputSchema: tool.inputSchema,
586
+ execute: async (args) => await Promise.resolve(tool.execute(args))
587
+ };
588
+ }
589
+ resolveGuardrails(guardrails) {
590
+ const maxTurns = guardrails?.maxTurns ?? __codemation_core.AgentGuardrailDefaults.maxTurns;
591
+ if (!Number.isInteger(maxTurns) || maxTurns < 1) throw new Error(`AIAgent maxTurns must be a positive integer. Received: ${String(maxTurns)}`);
592
+ return {
593
+ maxTurns,
594
+ onTurnLimitReached: guardrails?.onTurnLimitReached ?? __codemation_core.AgentGuardrailDefaults.onTurnLimitReached,
595
+ modelInvocationOptions: guardrails?.modelInvocationOptions
596
+ };
597
+ }
444
598
  };
445
599
  AIAgentNode = __decorate([
446
600
  (0, __codemation_core.node)({ packageName: "@codemation/core-nodes" }),
447
601
  __decorateParam(0, (0, __codemation_core.inject)(__codemation_core.CoreTokens.NodeResolver)),
448
602
  __decorateParam(1, (0, __codemation_core.inject)(__codemation_core.CoreTokens.CredentialSessionService)),
449
- __decorateMetadata("design:paramtypes", [Object, Object])
603
+ __decorateParam(2, (0, __codemation_core.inject)(NodeBackedToolRuntime)),
604
+ __decorateParam(3, (0, __codemation_core.inject)(AIAgentExecutionHelpersFactory)),
605
+ __decorateMetadata("design:paramtypes", [
606
+ Object,
607
+ Object,
608
+ typeof (_ref = typeof NodeBackedToolRuntime !== "undefined" && NodeBackedToolRuntime) === "function" ? _ref : Object,
609
+ typeof (_ref2 = typeof AIAgentExecutionHelpersFactory !== "undefined" && AIAgentExecutionHelpersFactory) === "function" ? _ref2 : Object
610
+ ])
450
611
  ], AIAgentNode);
451
612
 
452
613
  //#endregion
@@ -460,14 +621,21 @@ var AIAgent = class {
460
621
  type = AIAgentNode;
461
622
  execution = { hint: "local" };
462
623
  icon = "lucide:bot";
463
- constructor(name, systemMessage, userMessageFormatter, chatModel$1, tools = [], id, retryPolicy = __codemation_core.RetryPolicy.defaultForAiAgent) {
464
- this.name = name;
465
- this.systemMessage = systemMessage;
466
- this.userMessageFormatter = userMessageFormatter;
467
- this.chatModel = chatModel$1;
468
- this.tools = tools;
469
- this.id = id;
470
- this.retryPolicy = retryPolicy;
624
+ name;
625
+ messages;
626
+ chatModel;
627
+ tools;
628
+ id;
629
+ retryPolicy;
630
+ guardrails;
631
+ constructor(options) {
632
+ this.name = options.name;
633
+ this.messages = options.messages;
634
+ this.chatModel = options.chatModel;
635
+ this.tools = options.tools ?? [];
636
+ this.id = options.id;
637
+ this.retryPolicy = options.retryPolicy ?? __codemation_core.RetryPolicy.defaultForAiAgent;
638
+ this.guardrails = options.guardrails;
471
639
  }
472
640
  };
473
641
 
@@ -1143,6 +1311,12 @@ var ConnectionCredentialNodeConfigFactory = class {
1143
1311
  //#endregion
1144
1312
  exports.AIAgent = AIAgent;
1145
1313
  exports.AIAgentConnectionWorkflowExpander = AIAgentConnectionWorkflowExpander;
1314
+ Object.defineProperty(exports, 'AIAgentExecutionHelpersFactory', {
1315
+ enumerable: true,
1316
+ get: function () {
1317
+ return AIAgentExecutionHelpersFactory;
1318
+ }
1319
+ });
1146
1320
  Object.defineProperty(exports, 'AIAgentNode', {
1147
1321
  enumerable: true,
1148
1322
  get: function () {