UNPKG

@artinet/sdk

Version:

A TypeScript SDK for building collaborative AI agents.

124 lines (123 loc) 4.5 kB
/** * Copyright 2025 The Artinet Project * SPDX-License-Identifier: Apache-2.0 */ /** * @fileoverview A2A Agent Builder and Execution Engine Factory * * This module provides a fluent builder API for constructing A2A agents and * execution engines. It enables declarative definition of multi-step agent * workflows with type-safe step composition and automatic execution orchestration. * * **Key Features:** * - Fluent API with method chaining (`.text()`, `.data()`, `.file()`, etc.) * - Type-safe argument passing between steps via `args` carry pattern * - Multiple output types: text, file, data, message, artifact, status, task * - Agent-to-agent orchestration via `.sendMessage()` * - Static value shortcuts for simple steps * - Step skipping via `skip()` function * * **Basic Usage:** * ```typescript * import { cr8 } from "@artinet/sdk"; * * const agent = cr8("MyAgent") * .text(({ content }) => `You said: ${content}`) * .data(({ content }) => ({ length: content?.length })) * .agent; * ``` * * @module A2ABuilder * @version 0.6 * @since 0.5.6 * @author The Artinet Project */ import { A2A } from "../types/index.js"; import * as transform from './transform.js'; import { describe } from './index.js'; import { extractTextContent } from '../services/a2a/helpers/content.js'; import { logger } from "../config/index.js"; import { formatJson } from "../utils/utils.js"; import { isProcessing } from "../utils/constants.js"; /** * Creates an agent execution engine from a list of workflow steps. * * This function transforms a list of resolved step definitions into an executable * A2A engine that processes contexts through the defined workflow. The engine * is an async generator that yields updates as each step completes. * * **Execution Flow:** * 1. Yields "submitted" status update * 2. Executes each step in order, yielding transformed results * 3. Passes carried args from one step to the next * 4. Yields final task on completion * * @param stepsList - Array of resolved workflow steps (from AgentFactory.steps) * @returns A2A.Engine async generator function * @throws Error if stepsList is empty * * @example * ```typescript * // Typically accessed via AgentFactory * const engine = cr8("MyAgent") * .text("Hello") * .data({ timestamp: Date.now() }) * .engine; * * // Or create manually from steps * const engine = createStepEngine(factory.steps); * * // Execute the engine * for await (const update of engine(context)) { * console.log(update.kind, update); * } * ``` * * @public * @since 0.5.6 */ export function createStepEngine(stepsList) { if (stepsList.length === 0) { throw new Error('No steps provided'); } return async function* (context) { logger.info(`engine[context:${context.contextId}]: starting`); logger.debug(`engine[context:${context.contextId}]: taskId: ${context.taskId}`); const content = extractTextContent(context.userMessage); let _skipStep = false; const input = { message: context.messages, context: context, content: content, skip: () => { _skipStep = true; return; }, }; const submitted = describe.update.submitted({ contextId: context.contextId, taskId: context.taskId, }); logger.debug(`engine[context:${context.contextId}]: submitted`); yield submitted; for (const step of stepsList) { if (await context.isCancelled()) { break; } logger.debug(`engine[context:${context.contextId}]: executing step[${step.id}]: ${step.kind}`); const ret = await step.step({ ...input }); if (_skipStep) { _skipStep = false; logger.debug(`engine[context:${context.contextId}]: skipping step[${step.id}]`); continue; } logger.debug(`engine[context:${context.contextId}]: transforming step[${step.id}]`); const carried = yield* transform.Reply(ret, context, step.handler); input.args = carried; } const task = await context.getTask(); logger.debug(`engine[context:${context.contextId}]: completed task[${task.id}]: ${formatJson(task)}`); task.status.state = isProcessing(task.status.state) ? A2A.TaskState.completed : task.status.state; yield task; }; }