screwdriver-wrenchAdvanced Patterns

Pipeline events, debugging tools, performance optimization, error handling, and best practices for production use.

🎬 Pipeline Events

Pipelines emit events during execution that you can intercept for monitoring, auditing, and debugging.

Available Events

Event
When
Data

beforeAIPipelineRun

Before pipeline execution

{ sequence, name, stepCount, steps, input, params, options }

afterAIPipelineRun

After pipeline execution

{ sequence, name, stepCount, steps, input, result, executionTime }

Event Interception

BoxRegisterInterceptor( {
    interceptorObject: {
        beforeAIPipelineRun: ( event, interceptData ) => {
            println( "Pipeline starting: #interceptData.name#" )
            println( "Steps: #interceptData.stepCount#" )
        },
        afterAIPipelineRun: ( event, interceptData ) => {
            println( "Pipeline completed: #interceptData.name#" )
            println( "Time: #interceptData.executionTime#ms" )
        }
    }
} )

pipeline = aiMessage().user( "Hello" ).toDefaultModel()
result   = pipeline.run()
// Console output:
// Pipeline starting: AiRunnableSequence
// Steps: 2
// Pipeline completed: AiRunnableSequence
// Time: 1543ms

Use cases: performance monitoring, cost tracking (count tokens), error auditing, security logging.


🐛 Debugging Pipelines

Use .print() to see exactly what steps are in a pipeline:

Inspect Steps

Get detailed information about each step:

Step-by-Step Execution

Execute each step manually to isolate problems:


⚡ Performance Optimization

Choose the Right Model

Use cheaper/faster models for simple tasks — reserve powerful models for complex reasoning:

Minimize Transform Steps

Combine transformations where possible:

Cache Expensive Results

Avoid repeated AI calls for the same input:


🔒 Error Handling

Try-Catch

Wrap pipeline execution to handle AI failures gracefully:

Graceful Degradation

Provide rule-based fallbacks when AI is unavailable:

Validation Steps

Validate at every critical boundary:


📚 Best Practices

Design Principles

Single Responsibility — each step does one thing well ✅ Immutability — never modify pipeline state during execution ✅ Composition — build complex workflows from simple components ✅ Reusability — design pipelines as parameterized templates ✅ Explicit > Implicit — be clear about each data transformation

Common Patterns

Anti-Patterns to Avoid

Overly long pipelines (>10 steps) — break into named sub-pipelines ❌ Side effects in transforms — keep transforms pure (no DB writes, no external calls) ❌ Tight coupling — don't hardcode provider-specific logic inside transforms ❌ Missing error handling — always handle AI failures gracefully ❌ Ignoring performance — profile expensive operations before deploying


⚡ Async Pipeline Execution

Every IAiRunnable — including full pipeline sequences — exposes runAsync(), which dispatches execution to the io-tasks virtual thread pool and returns a BoxFuture. This lets you kick off expensive AI calls without blocking the current thread.

You can also use .then() for a callback pattern:

Running Multiple Pipelines Concurrently

The real power of runAsync() is running multiple independent pipelines at the same time:


🔀 Parallel Pipelines with aiParallel()

aiParallel() is purpose-built for fan-out scenarios: send the same input to multiple named runnables concurrently and receive all results in a single named struct.

Because AiRunnableParallel implements IAiRunnable, it composes naturally into larger pipelines with .transform() or .to():

Model Evaluation / A/B Testing

aiParallel() makes comparing outputs across providers or prompt variants trivial:


Last updated