Headroom Pipeline Lifecycle Explained: Setup, Post-Send, and Response Received
The Headroom pipeline lifecycle consists of three canonical stages—Setup, Post-Send, and Response Received—defined by the PipelineStage enum in headroom/pipeline.py, allowing extensions to observe and mutate requests via PipelineEvent emissions at each transition point.
Headroom implements a canonical pipeline lifecycle that orchestrates every request from initialization through response delivery. This lifecycle is defined by the PipelineStage enum in the core module headroom/pipeline.py, where each stage emits a PipelineEvent that extension plug-ins can observe and mutate. Understanding these three stages—Setup, Post-Send, and Response Received—is essential for building custom extensions that intercept, log, or transform LLM requests and responses.
The Three Stages of the Headroom Pipeline Lifecycle
The Headroom pipeline lifecycle provides three distinct interception points for extensions. Each stage represents a specific phase in the request journey, from client initialization through final response delivery.
Setup Stage: Pipeline Initialization
The Setup stage (PipelineStage.SETUP) executes immediately after a HeadroomClient instance is created, before any request processing begins. This stage initializes the pipeline, loads configuration, and discovers registered extensions via the entry-point group headroom.pipeline_extension.
During Setup, the PipelineExtensionManager constructs the default transform list and instantiates extensions. According to the source code in headroom/client.py, this occurs during client initialization when the manager is built and attached to the request-handling code.
Extensions can use this stage to pre-populate configuration, register custom transforms, or perform one-time warm-up work such as loading models or establishing persistent connections.
Post-Send Stage: Outbound Request Observation
The Post-Send stage (PipelineStage.POST_SEND) triggers after the transformed request body has been sent to the upstream provider (OpenAI, Anthropic, etc.) but before the response returns to the caller. This stage emits a PipelineEvent containing the final request payload, tool definitions, and request metadata.
In headroom/proxy/handlers/openai.py, lines 2153-2190 demonstrate this emission: after the body is prepared, the handler calls pipeline_extensions.emit(PipelineStage.POST_SEND, …). This gives extensions a final opportunity to log, audit, or decorate the outbound request, such as adding custom headers or triggering distributed tracing.
Response Received Stage: Inbound Response Processing
The Response Received stage (PipelineStage.RESPONSE_RECEIVED) fires immediately after the upstream provider's response has been received and decoded (JSON or streaming SSE). This stage emits an event carrying the raw response, status code, and additional metadata.
As implemented in headroom/proxy/handlers/openai.py (lines 2200-2208), the handler emits this event after a non-streaming call via pipeline_extensions.emit(PipelineStage.RESPONSE_RECEIVED, …). Extensions can inspect, modify, or log the response before it reaches the SDK user, enabling use cases like error translation, compliance tagging, or PII redaction.
How the Stages Connect
The Headroom pipeline lifecycle follows a deterministic sequence:
-
Setup – When
HeadroomClient.__init__executes,PipelineExtensionManagerdiscovers extensions viadiscover_pipeline_extensions()and attaches the manager to request-handling code. -
Transform Processing – The request passes through the transform pipeline (
TransformPipeline.applyinheadroom/transforms/pipeline.py), which mutates messages without emitting lifecycle events. -
Post-Send – After the transformed request is handed to the upstream LLM provider, the
POST_SENDevent fires, providing the last chance to observe the outbound payload. -
Response Handling – When the provider's reply arrives and is parsed, the
RESPONSE_RECEIVEDevent fires, allowing inspection of the inbound payload before returning to the caller.
The lifecycle is deliberately stable; external plug-ins can rely on the PipelineStage enum values without worrying about internal refactors.
Implementing Pipeline Extensions
Extensions implement the PipelineExtension base class and override on_pipeline_event to handle specific stages.
Logging the Request After Send
This extension logs model and token information during the Post-Send stage:
# my_extension.py
from headroom.pipeline import PipelineStage, PipelineEvent, PipelineExtension
class LogPostSend(PipelineExtension):
def on_pipeline_event(self, event: PipelineEvent) -> PipelineEvent | None:
if event.stage == PipelineStage.POST_SEND:
# `event.messages` holds the transformed request payload
print(f"[POST_SEND] model={event.model} tokens={len(event.messages)}")
return None
Register via Python entry points in setup.cfg:
[options.entry_points]
headroom.pipeline_extension =
log_post_send = my_extension:LogPostSend
Injecting Custom Headers
This extension adds headers after the request is sent:
class HeaderInjector(PipelineExtension):
def on_pipeline_event(self, event: PipelineEvent) -> PipelineEvent | None:
if event.stage == PipelineStage.POST_SEND:
# Mutate the outbound headers in-place
if event.headers is None:
event.headers = {}
event.headers["X-Request-Id"] = event.request_id
return event
Modifying the Response Body
This extension strips debug information from responses:
class ResponseSanitizer(PipelineExtension):
def on_pipeline_event(self, event: PipelineEvent) -> PipelineEvent | None:
if event.stage == PipelineStage.RESPONSE_RECEIVED:
# Example: strip any internal debug fields
if isinstance(event.response, dict):
event.response.pop("debug_info", None)
return event
Key Implementation Files
Understanding the Headroom pipeline lifecycle requires familiarity with these core files:
-
headroom/pipeline.py– Defines thePipelineStageenum (including SETUP, POST_SEND, RESPONSE_RECEIVED) and thePipelineExtensionManagerthat dispatches events to registered extensions. -
headroom/proxy/handlers/openai.py– Demonstrates real-world emission of POST_SEND (lines 2153-2190) and RESPONSE_RECEIVED (lines 2200-2208) events during OpenAI request processing. -
headroom/transforms/pipeline.py– Implements the coreTransformPipelinethat runs between SETUP and POST_SEND, providing token-aware compression and message transformation. -
headroom/client.py– Constructs thePipelineExtensionManagerduring client initialization, serving as the entry point for the SETUP stage. -
headroom/transforms/base.py– Provides the base class for all transforms, essential when building custom transforms that participate in the pipeline between SETUP and POST_SEND.
Summary
- The Headroom pipeline lifecycle comprises three canonical stages: Setup, Post-Send, and Response Received, defined in
headroom/pipeline.py. - Setup initializes extensions and configuration when
HeadroomClientis instantiated. - Post-Send fires after the request is transmitted to the upstream provider, allowing observation and mutation of outbound payloads.
- Response Received triggers after the provider's response is decoded, enabling inspection and modification before returning to the caller.
- Extensions register via the
headroom.pipeline_extensionentry point and implementon_pipeline_eventto handle specific stages. - The lifecycle is stable across versions, with explicit emission points in
headroom/proxy/handlers/openai.py.
Frequently Asked Questions
When does the Setup stage run in the Headroom pipeline lifecycle?
The Setup stage runs immediately after HeadroomClient instantiation, before any request is processed. During this phase, the PipelineExtensionManager discovers extensions via discover_pipeline_extensions() and builds the default transform list. This occurs in headroom/client.py during client initialization, making it the ideal time for one-time configuration and warm-up activities.
How do I register a custom extension for the Post-Send stage?
Register your extension by defining a Python entry point in your package configuration under the group headroom.pipeline_extension. Your class should inherit from PipelineExtension and check event.stage == PipelineStage.POST_SEND in the on_pipeline_event method. The extension will automatically receive events after the request is sent to the upstream provider.
Can I modify the response body in the Response Received stage?
Yes, the Response Received stage allows mutation of the response before it reaches the SDK user. The PipelineEvent passed to your extension contains the response attribute, which you can modify in-place or replace entirely. This enables use cases like stripping debug fields, redacting sensitive information, or translating error formats.
What happens between the Setup and Post-Send stages?
Between Setup and Post-Send, the request passes through the Transform Pipeline (TransformPipeline.apply in headroom/transforms/pipeline.py). This phase applies token-aware compression, content routing, and message transformations. Unlike the lifecycle stages, the transform pipeline does not emit events; it simply mutates the request messages before they are forwarded to the upstream provider.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →