zoobzio December 12, 2025 Edit this page

Recipe: Library Resilience

When building a library where the domain type T is known, you can expose pipz resilience patterns to consumers via a clean With* functional options API.

The Pattern

This recipe demonstrates how zyn (an LLM orchestration library) uses pipz to give consumers composable resilience without exposing pipz internals.

Core Concept

// The Option type wraps a pipeline with additional behavior
type Option func(pipz.Chainable[*Request]) pipz.Chainable[*Request]

// Each With* function returns an Option that wraps the pipeline
func WithRetry(attempts int) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewRetry(retryID, p, attempts)
    }
}

Consumers compose options at construction time:

client, _ := mylib.NewClient(
    mylib.WithRetry(3),
    mylib.WithTimeout(10*time.Second),
    mylib.WithCircuitBreaker(5, 30*time.Second),
)

The result is a pipeline: CircuitBreaker(Timeout(Retry(base)))

Implementation

Define Your Request Type

// Your library's request type
type Request struct {
    Input    interface{}
    Output   interface{}
    Metadata map[string]string
}

Define Resilience Identities

// Package-level identities for each resilience pattern
var (
    retryID          = pipz.NewIdentity("mylib:retry", "Retries failed requests")
    backoffID        = pipz.NewIdentity("mylib:backoff", "Retries with exponential backoff")
    timeoutID        = pipz.NewIdentity("mylib:timeout", "Enforces request timeout")
    circuitBreakerID = pipz.NewIdentity("mylib:circuit-breaker", "Prevents cascading failures")
    rateLimitID      = pipz.NewIdentity("mylib:rate-limit", "Limits request rate")
    fallbackID       = pipz.NewIdentity("mylib:fallback", "Falls back to alternative on failure")
    errorHandlerID   = pipz.NewIdentity("mylib:error-handler", "Handles request errors")
)

Define the Option Type

// Option wraps a pipeline with additional behavior
type Option func(pipz.Chainable[*Request]) pipz.Chainable[*Request]

Implement With* Functions

// WithRetry retries failed requests up to maxAttempts times
func WithRetry(maxAttempts int) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewRetry(retryID, p, maxAttempts)
    }
}

// WithBackoff retries with exponential backoff
func WithBackoff(maxAttempts int, baseDelay time.Duration) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewBackoff(backoffID, p, maxAttempts, baseDelay)
    }
}

// WithTimeout enforces a maximum request duration
func WithTimeout(duration time.Duration) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewTimeout(timeoutID, p, duration)
    }
}

// WithCircuitBreaker prevents cascading failures
func WithCircuitBreaker(failures int, recovery time.Duration) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewCircuitBreaker(circuitBreakerID, p, failures, recovery)
    }
}

// WithRateLimit limits request rate
func WithRateLimit(rps float64, burst int) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewRateLimiter(rateLimitID, rps, burst, p)
    }
}

// WithFallback uses an alternative client on failure
func WithFallback(fallback Client) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewFallback(fallbackID, p, fallback.pipeline())
    }
}

// WithErrorHandler provides custom error handling
func WithErrorHandler(handler pipz.Chainable[*pipz.Error[*Request]]) Option {
    return func(p pipz.Chainable[*Request]) pipz.Chainable[*Request] {
        return pipz.NewHandle(errorHandlerID, p, handler)
    }
}

Apply Options in Constructor

// Client is your library's main type
type Client struct {
    pipe pipz.Chainable[*Request]
}

// NewClient creates a client with optional resilience configuration
func NewClient(backend Backend, opts ...Option) *Client {
    // Create the base pipeline (your core logic)
    base := newTerminal(backend)

    // Apply each option, wrapping the pipeline
    pipe := pipz.Chainable[*Request](base)
    for _, opt := range opts {
        pipe = opt(pipe)
    }

    return &Client{pipe: pipe}
}

// Execute processes a request through the configured pipeline
func (c *Client) Execute(ctx context.Context, req *Request) (*Request, error) {
    return c.pipe.Process(ctx, req)
}

// pipeline exposes the internal pipeline for composition (e.g., WithFallback)
func (c *Client) pipeline() pipz.Chainable[*Request] {
    return c.pipe
}

Create the Base Terminal

var terminalID = pipz.NewIdentity("mylib:terminal", "Executes request against backend")

func newTerminal(backend Backend) pipz.Chainable[*Request] {
    return pipz.Apply(terminalID, func(ctx context.Context, req *Request) (*Request, error) {
        result, err := backend.Call(ctx, req.Input)
        if err != nil {
            return req, err
        }
        req.Output = result
        return req, nil
    })
}

Usage Examples

Basic Retry

client := mylib.NewClient(backend,
    mylib.WithRetry(3),
)

Production Configuration

client := mylib.NewClient(backend,
    mylib.WithRetry(3),
    mylib.WithTimeout(10*time.Second),
    mylib.WithCircuitBreaker(5, 30*time.Second),
    mylib.WithRateLimit(100, 10),
)

Options are applied in order, creating: RateLimit(CircuitBreaker(Timeout(Retry(terminal))))

With Fallback

primary := mylib.NewClient(primaryBackend,
    mylib.WithTimeout(5*time.Second),
)

fallback := mylib.NewClient(fallbackBackend,
    mylib.WithTimeout(10*time.Second),
)

resilient := mylib.NewClient(primaryBackend,
    mylib.WithTimeout(5*time.Second),
    mylib.WithFallback(fallback),
)

Real-World Example: zyn

zyn implements this pattern for LLM operations:

// From zyn - creating a classifier with resilience
classifier, _ := zyn.Classification(
    "What type of email is this?",
    []string{"spam", "urgent", "newsletter", "personal"},
    provider,
    zyn.WithRetry(3),
    zyn.WithTimeout(10*time.Second),
    zyn.WithCircuitBreaker(5, 30*time.Second),
)

// Execute with full resilience
category, _ := classifier.Fire(ctx, session, "URGENT: Your account suspended!")

zyn provides 8 synapse types (Binary, Classification, Extraction, Transform, etc.), each accepting the same ...Option parameter. The resilience layer is completely orthogonal to the LLM logic.

Benefits

  1. Clean API — Consumers see WithRetry(3), not pipz.NewRetry(id, p, 3)
  2. Composable — Options combine naturally in any order
  3. Encapsulated — pipz is an implementation detail, not a public dependency
  4. Type-Safe — The generic T is fixed to your domain type
  5. Testable — Each option can be tested in isolation

Key Insight

By fixing pipz.Chainable[T] to your domain type (*Request, *SynapseRequest, etc.), you create a resilience vocabulary specific to your library. Consumers get production-grade resilience without learning pipz.

See Also