← All examples
// Production Control

Cost-Tiered Pipeline

Demonstrates: - Running the same four-stage runTasks() pipeline twice - Assigning different models per agent within one pipeline - Capturing token usage per model via onTrace - Estimating USD cost from provider-specific model pricing

Enterprise support
01 Run it
OMA APIsOpenMultiAgent
Production Control559 lines

From a clone of the repo — this exact file:

terminal
npx tsx packages/core/examples/patterns/cost-tiered-pipeline.ts
Prerequisites
  • Set one of:
  • - ANTHROPIC_API_KEY
  • - OPENAI_API_KEY
Open the complete, synchronized source · 559 lines

The complete example, synchronized from the pinned Framework commit.

patterns/cost-tiered-pipeline.ts
/**
* Cost-Tiered Pipeline
*
* Demonstrates:
* - Running the same four-stage `runTasks()` pipeline twice
* - Assigning different models per agent within one pipeline
* - Capturing token usage per model via `onTrace`
* - Estimating USD cost from provider-specific model pricing
*
* Run:
* npx tsx packages/core/examples/patterns/cost-tiered-pipeline.ts
*
* Prerequisites:
* Set one of:
* - ANTHROPIC_API_KEY
* - OPENAI_API_KEY
*
* Optional:
* - LLM_PROVIDER=anthropic|openai
* - ANTHROPIC_BASE_URL for Anthropic-compatible relays
* - OPENAI_BASE_URL for OpenAI-compatible relays
*/
 
import { OpenMultiAgent } from '../../src/index.js'
import type { AgentConfig, OrchestratorEvent, TokenUsage, TraceEvent } from '../../src/types.js'
 
// ---------------------------------------------------------------------------
// Pricing
// ---------------------------------------------------------------------------
 
const PRICING_AS_OF = '2026-04-24'
 
const PRICING = {
// Anthropic + OpenAI API pricing, as of 2026-04-24, USD per 1M tokens
'claude-opus-4-7': { input: 5, output: 25 },
'claude-sonnet-4-6': { input: 3, output: 15 },
'claude-haiku-4-5': { input: 1, output: 5 },
'gpt-5.5': { input: 5, output: 30 },
'gpt-5.4': { input: 2.5, output: 15 },
'gpt-5.4-mini': { input: 0.75, output: 4.5 },
} as const
 
type PricedModel = keyof typeof PRICING
type UsageByModel = Partial<Record<PricedModel, TokenUsage>>
type ProviderId = 'anthropic' | 'openai'
type ProviderConfig = Pick<AgentConfig, 'provider' | 'model' | 'apiKey' | 'baseURL'>
type AgentRunSummary = Map<string, { success: boolean, tokenUsage: TokenUsage }>
type PipelineAssignments = {
researcher: ProviderConfig
classifier: ProviderConfig
drafter: ProviderConfig
reviewer: ProviderConfig
}
type ScenarioResult = {
label: string
assignments: PipelineAssignments
agentResults: AgentRunSummary
usageByModel: UsageByModel
totalTokenUsage: TokenUsage
costUsd: number
elapsedMs: number
finalBrief: string
}
 
// ---------------------------------------------------------------------------
// Topic
// ---------------------------------------------------------------------------
 
const TOPIC = 'Launch a developer-focused AI code review assistant for 200-800 engineer software teams'
 
// ---------------------------------------------------------------------------
// Prompts
// ---------------------------------------------------------------------------
 
const RESEARCHER_PROMPT = `You are a product researcher preparing source material for a launch brief.
Focus on:
- target buyer and user personas
- pain points in code review workflows
- market landscape and competitive differentiators
- adoption risks and rollout constraints
Write structured markdown in 220-300 words. Be concrete and specific.`
 
const CLASSIFIER_PROMPT = `You are a product strategist turning raw research into decision-ready structure.
Organize the upstream research into four sections:
1. Audience
2. Risks
3. Differentiators
4. Open Questions
Write 180-250 words in markdown. Keep it crisp and managerial.`
 
const DRAFTER_PROMPT = `You are a senior product marketer writing a launch brief.
Use the prior task outputs from shared memory and produce a polished brief with:
- executive summary
- target audience
- product positioning
- go-to-market risks
- recommended launch angle
Write 380-500 words in markdown.`
 
const REVIEWER_PROMPT = `You are an executive reviewer. Rewrite the brief for clarity, internal consistency,
and sharper prioritization. Keep the strongest claims, remove repetition, and make the
recommendation decisive.
Return the final brief in 350-450 words using markdown headings.`
 
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
 
function zeroUsage(): TokenUsage {
return { input_tokens: 0, output_tokens: 0 }
}
 
function addUsage(a: TokenUsage, b: TokenUsage): TokenUsage {
return {
input_tokens: a.input_tokens + b.input_tokens,
output_tokens: a.output_tokens + b.output_tokens,
}
}
 
function isPricedModel(model: string): model is PricedModel {
return model in PRICING
}
 
function createUsageCollector(): {
usageByModel: UsageByModel
handleTrace: (event: TraceEvent) => void
} {
const usageByModel: UsageByModel = {}
 
function handleTrace(event: TraceEvent): void {
if (event.type !== 'llm_call' || !isPricedModel(event.model)) return
usageByModel[event.model] = addUsage(usageByModel[event.model] ?? zeroUsage(), event.tokens)
}
 
return { usageByModel, handleTrace }
}
 
function estimateCostUsd(usageByModel: UsageByModel): number {
return Object.entries(usageByModel).reduce((sum, [model, usage]) => {
if (!usage || !isPricedModel(model)) return sum
const pricing = PRICING[model]
 
return sum + (
(usage.input_tokens / 1_000_000) * pricing.input +
(usage.output_tokens / 1_000_000) * pricing.output
)
}, 0)
}
 
function formatUsd(value: number): string {
return `$${value.toFixed(4)}`
}
 
function formatSeconds(ms: number): string {
return `${(ms / 1000).toFixed(2)}s`
}
 
function formatPercent(value: number): string {
return `${(value * 100).toFixed(1)}%`
}
 
function inferProvider(): ProviderId {
if (process.env.OPENAI_API_KEY) return 'openai'
if (process.env.ANTHROPIC_API_KEY) return 'anthropic'
 
throw new Error('Set OPENAI_API_KEY or ANTHROPIC_API_KEY to run this example.')
}
 
function getSelectedProvider(): ProviderId {
const requested = process.env.LLM_PROVIDER?.trim().toLowerCase()
if (!requested) return inferProvider()
 
if (requested === 'anthropic' || requested === 'openai') {
return requested
}
 
throw new Error(
`Unsupported LLM_PROVIDER="${process.env.LLM_PROVIDER}". Use one of: anthropic, openai.`,
)
}
 
function getProviderConfigs(provider: ProviderId): {
baselineLabel: string
tieredLabel: string
endpointLabel: string
flagship: ProviderConfig
mid: ProviderConfig
cheap: ProviderConfig
} {
switch (provider) {
case 'openai': {
const apiKey = process.env.OPENAI_API_KEY
if (!apiKey) {
throw new Error('LLM_PROVIDER=openai requires OPENAI_API_KEY.')
}
 
const baseURL = process.env.OPENAI_BASE_URL
 
return {
baselineLabel: 'All GPT-5.5 baseline',
tieredLabel: 'Tiered GPT mix',
endpointLabel: baseURL ?? 'OpenAI official API',
flagship: {
provider: 'openai',
model: 'gpt-5.5',
apiKey,
baseURL,
},
mid: {
provider: 'openai',
model: 'gpt-5.4',
apiKey,
baseURL,
},
cheap: {
provider: 'openai',
model: 'gpt-5.4-mini',
apiKey,
baseURL,
},
}
}
 
case 'anthropic':
default: {
const apiKey = process.env.ANTHROPIC_API_KEY
if (!apiKey) {
throw new Error('LLM_PROVIDER=anthropic requires ANTHROPIC_API_KEY.')
}
 
const baseURL = process.env.ANTHROPIC_BASE_URL
 
return {
baselineLabel: 'All Opus baseline',
tieredLabel: 'Tiered Claude mix',
endpointLabel: baseURL ?? 'Anthropic official API',
flagship: {
provider: 'anthropic',
model: 'claude-opus-4-7',
apiKey,
baseURL,
},
mid: {
provider: 'anthropic',
model: 'claude-sonnet-4-6',
apiKey,
baseURL,
},
cheap: {
provider: 'anthropic',
model: 'claude-haiku-4-5',
apiKey,
baseURL,
},
}
}
}
}
 
function createProgressHandler(label: string): (event: OrchestratorEvent) => void {
return (event: OrchestratorEvent): void => {
if (event.type === 'task_start') {
console.log(` [START] ${label} :: ${event.task ?? ''} → ${event.agent ?? ''}`)
}
if (event.type === 'task_complete') {
console.log(` [DONE] ${label} :: ${event.task ?? ''}`)
}
}
}
 
function createAgents(assignments: PipelineAssignments): AgentConfig[] {
return [
{
name: 'researcher',
...assignments.researcher,
systemPrompt: RESEARCHER_PROMPT,
maxTurns: 2,
temperature: 0.2,
},
{
name: 'classifier',
...assignments.classifier,
systemPrompt: CLASSIFIER_PROMPT,
maxTurns: 2,
temperature: 0.2,
},
{
name: 'drafter',
...assignments.drafter,
systemPrompt: DRAFTER_PROMPT,
maxTurns: 2,
temperature: 0.3,
},
{
name: 'reviewer',
...assignments.reviewer,
systemPrompt: REVIEWER_PROMPT,
maxTurns: 2,
temperature: 0.2,
},
]
}
 
function describeAssignments(assignments: PipelineAssignments): string {
return [
`researcher=${assignments.researcher.model}`,
`classifier=${assignments.classifier.model}`,
`drafter=${assignments.drafter.model}`,
`reviewer=${assignments.reviewer.model}`,
].join(', ')
}
 
function printAgentTokenSummary(agentResults: AgentRunSummary): void {
console.log('Agent Token Summary')
console.log('-'.repeat(60))
 
for (const [name, run] of agentResults) {
const icon = run.success ? 'OK ' : 'FAIL'
const tokens = `in:${run.tokenUsage.input_tokens} out:${run.tokenUsage.output_tokens}`
console.log(` [${icon}] ${name.padEnd(12)} ${tokens}`)
}
 
console.log()
}
 
function printModelCostBreakdown(usageByModel: UsageByModel): void {
console.log('Model Cost Breakdown')
console.log('-'.repeat(60))
console.log('| Model | Input | Output | USD cost |')
console.log('|-------|------:|-------:|---------:|')
 
for (const [model, usage] of Object.entries(usageByModel)) {
if (!usage || !isPricedModel(model)) continue
const costUsd =
(usage.input_tokens / 1_000_000) * PRICING[model].input +
(usage.output_tokens / 1_000_000) * PRICING[model].output
 
console.log(
`| ${model} | ${usage.input_tokens} | ${usage.output_tokens} | ${formatUsd(costUsd)} |`,
)
}
 
console.log()
}
 
function printComparisonTable(runs: ScenarioResult[]): void {
console.log('Comparison')
console.log('-'.repeat(60))
console.log('| Run | Input | Output | USD cost | Wall time |')
console.log('|-----|------:|-------:|---------:|----------:|')
 
for (const run of runs) {
console.log(
`| ${run.label} | ${run.totalTokenUsage.input_tokens} | ${run.totalTokenUsage.output_tokens} | ${formatUsd(run.costUsd)} | ${formatSeconds(run.elapsedMs)} |`,
)
}
 
console.log()
}
 
function printFinalSummaryTable(runs: ScenarioResult[]): void {
console.log('Final Summary')
console.log('-'.repeat(60))
console.log('| Run | Models | Input | Output | USD cost | Wall time |')
console.log('|-----|--------|------:|-------:|---------:|----------:|')
 
for (const run of runs) {
console.log(
`| ${run.label} | ${describeAssignments(run.assignments)} | ${run.totalTokenUsage.input_tokens} | ${run.totalTokenUsage.output_tokens} | ${formatUsd(run.costUsd)} | ${formatSeconds(run.elapsedMs)} |`,
)
}
 
console.log()
}
 
function printScenarioDetails(run: ScenarioResult): void {
console.log('='.repeat(60))
console.log(run.label.toUpperCase())
console.log('='.repeat(60))
console.log(`Models: ${describeAssignments(run.assignments)}`)
console.log(
`Tokens — input: ${run.totalTokenUsage.input_tokens}, output: ${run.totalTokenUsage.output_tokens}`,
)
console.log(`Estimated USD cost — ${formatUsd(run.costUsd)}`)
console.log(`Wall time — ${formatSeconds(run.elapsedMs)}`)
console.log()
 
printAgentTokenSummary(run.agentResults)
printModelCostBreakdown(run.usageByModel)
 
console.log('Final Brief')
console.log('-'.repeat(60))
console.log(run.finalBrief)
console.log()
}
 
async function runScenario(
label: string,
assignments: PipelineAssignments,
tasks: Array<{
title: string
description: string
assignee: string
dependsOn?: string[]
}>,
): Promise<ScenarioResult> {
const agents = createAgents(assignments)
const usageCollector = createUsageCollector()
const orchestrator = new OpenMultiAgent({
defaultProvider: assignments.reviewer.provider,
defaultModel: assignments.reviewer.model,
defaultBaseURL: assignments.reviewer.baseURL,
defaultApiKey: assignments.reviewer.apiKey,
onProgress: createProgressHandler(label),
onTrace: usageCollector.handleTrace,
})
 
const team = orchestrator.createTeam(`cost-tiered-pipeline-${label.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-')}`, {
name: `cost-tiered-pipeline-${label.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-')}`,
agents,
sharedMemory: true,
})
 
console.log(`Run: ${label}`)
console.log(`Models: ${describeAssignments(assignments)}`)
console.log()
 
const startedAt = performance.now()
const result = await orchestrator.runTasks(team, tasks)
const elapsedMs = performance.now() - startedAt
 
if (!result.success) {
console.error(`${label} failed.`)
for (const [name, run] of result.agentResults) {
if (!run.success) {
console.error(` ${name}: ${run.output}`)
}
}
process.exit(1)
}
 
const finalResult = result.agentResults.get('reviewer')
if (!finalResult?.success) {
console.error(`${label} did not produce a final reviewer output.`)
process.exit(1)
}
 
return {
label,
assignments,
agentResults: result.agentResults,
usageByModel: usageCollector.usageByModel,
totalTokenUsage: result.totalTokenUsage,
costUsd: estimateCostUsd(usageCollector.usageByModel),
elapsedMs,
finalBrief: finalResult.output,
}
}
 
// ---------------------------------------------------------------------------
// Provider resolution
// ---------------------------------------------------------------------------
 
const selectedProvider = getSelectedProvider()
const providerConfigs = getProviderConfigs(selectedProvider)
 
// ---------------------------------------------------------------------------
// Tasks — fixed four-stage pipeline
// ---------------------------------------------------------------------------
 
const tasks = [
{
title: 'Research launch context',
description: `Research ${TOPIC}. Focus on buyer pain points, review bottlenecks, team adoption concerns, competitor positioning, and measurable product value.`,
assignee: 'researcher',
},
{
title: 'Classify findings',
description: `Read the research from shared memory and classify it into audience, risks, differentiators, and open questions. Preserve concrete details and avoid generic filler.`,
assignee: 'classifier',
dependsOn: ['Research launch context'],
},
{
title: 'Draft launch brief',
description: `Using the structured findings, draft a substantial launch brief for leadership review. The brief should be decision-ready and exceed 300 words.`,
assignee: 'drafter',
dependsOn: ['Classify findings'],
},
{
title: 'Review final brief',
description: `Review the drafted brief, tighten the positioning, and return the final version leadership should read.`,
assignee: 'reviewer',
dependsOn: ['Draft launch brief'],
},
]
 
// ---------------------------------------------------------------------------
// Scenario assignments
// ---------------------------------------------------------------------------
 
const baselineAssignments: PipelineAssignments = {
researcher: providerConfigs.flagship,
classifier: providerConfigs.flagship,
drafter: providerConfigs.flagship,
reviewer: providerConfigs.flagship,
}
 
const tieredAssignments: PipelineAssignments = {
researcher: providerConfigs.cheap,
classifier: providerConfigs.cheap,
drafter: providerConfigs.mid,
reviewer: providerConfigs.flagship,
}
 
// ---------------------------------------------------------------------------
// Run
// ---------------------------------------------------------------------------
 
console.log('Cost-Tiered Pipeline')
console.log('='.repeat(60))
console.log(`Provider: ${selectedProvider}`)
console.log(`Endpoint: ${providerConfigs.endpointLabel}`)
console.log(`Pricing as of: ${PRICING_AS_OF}`)
console.log(`Topic: ${TOPIC}`)
console.log('Pipeline: researcher -> classifier -> drafter -> reviewer')
console.log()
 
const baselineRun = await runScenario(providerConfigs.baselineLabel, baselineAssignments, tasks)
console.log('-'.repeat(60))
const tieredRun = await runScenario(providerConfigs.tieredLabel, tieredAssignments, tasks)
 
// ---------------------------------------------------------------------------
// Output
// ---------------------------------------------------------------------------
 
console.log('\n' + '='.repeat(60))
printComparisonTable([baselineRun, tieredRun])
 
const savings = 1 - (tieredRun.costUsd / baselineRun.costUsd)
console.log(`Tiered savings vs baseline — ${formatPercent(savings)}`)
console.log()
 
printScenarioDetails(baselineRun)
printScenarioDetails(tieredRun)
 
console.log('='.repeat(60))
printFinalSummaryTable([baselineRun, tieredRun])
 
if (savings < 0.4) {
console.error(`ASSERTION FAILED: tiered savings ${Math.round(savings * 100)}% < 40%`)
process.exit(1)
}
 
console.log(`Savings assertion — OK (${formatPercent(savings)} >= 40.0%)`)
console.log('\nDone.')
View & edit on GitHub
// Enterprise

Taking this to production?

open-multi-agent is MIT-licensed and free to run yourself. When you need it delivered, integrated, or supported on a deadline, 元定义科技 (YuanASI) offers commercial delivery and support.

Enterprise support