From 6ace8635b185f9d86d86b179ef9e48a89abf6f61 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 3 Jul 2026 19:42:15 +0530 Subject: [PATCH] docs: add pipeline extension recipe --- docs/content/docs/configuration.mdx | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/content/docs/configuration.mdx b/docs/content/docs/configuration.mdx index fb232abb9..50ffc98e2 100644 --- a/docs/content/docs/configuration.mdx +++ b/docs/content/docs/configuration.mdx @@ -182,6 +182,39 @@ response = client.chat.completions.create( The `RollingWindowConfig`, `IntelligentContextConfig`, and `ScoringWeights` classes were removed in 0.9.x. Context management now happens automatically inside the pipeline. +## Pipeline Extensions + +Use a `headroom.pipeline_extension` entry point when you need to normalize or annotate requests before they leave Headroom. The `PRE_SEND` stage is the right place for provider-specific request cleanup, such as turning `content: null` into `content: ""` for upstreams that reject OpenAI-spec tool-call messages. + +```python +from headroom.pipeline import PipelineEvent, PipelineStage + + +class NormalizeNullContent: + def on_pipeline_event(self, event: PipelineEvent) -> PipelineEvent: + if event.stage is not PipelineStage.PRE_SEND or not event.messages: + return event + + for message in event.messages: + if ( + message.get("role") == "assistant" + and message.get("content") is None + and message.get("tool_calls") + ): + message["content"] = "" + + return event +``` + +Register it in `pyproject.toml`: + +```toml +[project.entry-points."headroom.pipeline_extension"] +normalize_null_content = "my_pkg.normalize:NormalizeNullContent" +``` + +If the upstream base URL itself must vary per request, use the `x-headroom-base-url` override header in addition to the normalization hook. + ## Proxy Configuration ### Command Line Options