From 62ad49afd580aa4773ac7dce5939094e1e9e67e9 Mon Sep 17 00:00:00 2001 From: Anurag Sharan <34830688+sharananurag998@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:45:04 +0530 Subject: [PATCH] feat(core): implement multi-agent patterns --- src/core/multi-agent.ts | 143 ++++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 2 files changed, 144 insertions(+) create mode 100644 src/core/multi-agent.ts diff --git a/src/core/multi-agent.ts b/src/core/multi-agent.ts new file mode 100644 index 0000000..411ed1d --- /dev/null +++ b/src/core/multi-agent.ts @@ -0,0 +1,143 @@ +import { v4 as uuidv4 } from 'uuid'; +import { run } from './engine'; +import { + RunConfig, + RunResult, + RunState, + createRunId, + createTraceId +} from './types'; + +// Helper to create initial run state for an agent +function createState( + agentName: string, + input: string, + context: Ctx +): RunState { + return { + runId: createRunId(uuidv4()), + traceId: createTraceId(uuidv4()), + messages: [{ role: 'user', content: input }], + currentAgentName: agentName, + context, + turnCount: 0 + }; +} + +function outputToString(output: any): string { + return typeof output === 'string' ? output : JSON.stringify(output); +} + +/** + * Sequential pipeline: A1 -> A2 -> A3 + */ +export async function runSequentialPipeline( + agentNames: readonly string[], + input: string, + context: Ctx, + config: RunConfig +): Promise> { + let currentInput = input; + let lastResult: RunResult | undefined; + + for (const name of agentNames) { + const state = createState(name, currentInput, context); + const result = await run(state, config); + lastResult = result; + if (result.outcome.status === 'error') { + return result; + } + currentInput = outputToString(result.outcome.output); + } + + return lastResult!; +} + +/** + * Parallel pipeline: A1 -> (A2, A3) -> A4 + */ +export async function runParallelPipeline( + first: string, + parallel: readonly [string, string], + final: string, + input: string, + context: Ctx, + config: RunConfig +): Promise> { + const firstState = createState(first, input, context); + const firstResult = await run(firstState, config); + if (firstResult.outcome.status === 'error') { + return firstResult; + } + const intermediate = outputToString(firstResult.outcome.output); + + const [name2, name3] = parallel; + const state2 = createState(name2, intermediate, context); + const state3 = createState(name3, intermediate, context); + const [res2, res3] = await Promise.all([ + run(state2, config), + run(state3, config) + ]); + if (res2.outcome.status === 'error') { + return res2; + } + if (res3.outcome.status === 'error') { + return res3; + } + const combined = `${outputToString(res2.outcome.output)}\n${outputToString(res3.outcome.output)}`; + + const finalState = createState(final, combined, context); + return await run(finalState, config); +} + +/** + * Coordinator pattern: A1 -> A2 (if condition) -> A3 else A4 -> A5 + */ +export async function runCoordinatorPipeline( + agents: { start: string; condition: string; onTrue: string; onFalse: string; end: string }, + condition: (outputFromA2: string) => boolean, + input: string, + context: Ctx, + config: RunConfig +): Promise> { + const startResult = await run(createState(agents.start, input, context), config); + if (startResult.outcome.status === 'error') return startResult; + const condInput = outputToString(startResult.outcome.output); + + const condResult = await run(createState(agents.condition, condInput, context), config); + if (condResult.outcome.status === 'error') return condResult; + const branchInput = outputToString(condResult.outcome.output); + + const nextAgent = condition(branchInput) ? agents.onTrue : agents.onFalse; + const branchResult = await run(createState(nextAgent, branchInput, context), config); + if (branchResult.outcome.status === 'error') return branchResult; + const finalInput = outputToString(branchResult.outcome.output); + + return await run(createState(agents.end, finalInput, context), config); +} + +/** + * Parallel redundant pattern: Query -> A1, A2 -> A3 + */ +export async function runParallelRedundant( + query: string, + agents: { parallel: [string, string]; evaluator: string }, + context: Ctx, + config: RunConfig +): Promise> { + const [a1, a2] = agents.parallel; + const state1 = createState(a1, query, context); + const state2 = createState(a2, query, context); + + const [res1, res2] = await Promise.all([ + run(state1, config), + run(state2, config) + ]); + if (res1.outcome.status === 'error') return res1; + if (res2.outcome.status === 'error') return res2; + + const evaluationInput = `Agent ${a1}: ${outputToString(res1.outcome.output)}\nAgent ${a2}: ${outputToString(res2.outcome.output)}`; + const evalState = createState(agents.evaluator, evaluationInput, context); + return await run(evalState, config); +} + diff --git a/src/index.ts b/src/index.ts index 15a3feb..736e44b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,6 +4,7 @@ export * from './core/tracing'; export * from './core/errors'; export * from './core/tool-results'; export * from './core/agent-as-tool'; +export * from './core/multi-agent'; export * from './providers/model'; // export * from './providers/mcp'; // Commented out for test compatibility