Middleware Reference

When to read this: you want the middleware catalog without opening one large page.

Middlewares run in registration order. They may:

  • transform a record or batch and pass it downstream
  • return None to drop a record
  • raise to route that record or batch through the configured failure path

For runtime-wide guarantees around ordering, checkpointing, and failure handling, see Runtime Guarantees.

Per-record middlewares

Batch middlewares

Arrow-native middlewares

Schema and AI

Writing a custom middleware

Subclass Middleware[T, U] and implement process():

from agora import Middleware, PipelineContext


class NormalizeMiddleware(Middleware[RawRecord, CleanRecord]):
    name = "normalize"

    async def process(self, record: RawRecord, ctx: PipelineContext) -> CleanRecord | None:
        if not record.name:
            return None
        return CleanRecord(
            id=record.id,
            name=record.name.strip().lower(),
        )

Use on_start() and on_stop() for setup and teardown, and raise to route the failing record or batch through the configured failure policy.

Writing a custom AI middleware

Subclass AIMiddleware[T] when the middleware needs prompt rendering, provider calls, caching, and structured AI error handling:

from agora.middlewares.ai.base import AIMiddleware


class SentimentMiddleware(AIMiddleware[Review]):
    name = "sentiment"

    async def process(self, record: Review, ctx: PipelineContext) -> Review | None:
        prompt = self._render_prompt(
            "Analyze sentiment of: {text}. Return JSON: {\"sentiment\": \"positive|negative|neutral\"}",
            record,
        )
        resp = await self._cached_complete(prompt, ctx=ctx)
        data = self._parse_json(resp.content)
        return record.model_copy(update=data)

Always pass ctx=ctx to _cached_complete() so AI metrics and tracing stay correct.