DEV Community

Saul Fernandez
Saul Fernandez

Posted on

How I Slashed My AI Agent Token Costs by 90% (And Survived a Multi-Provider Docker Nightmare)

My autonomous coding agent setup in Discord was running beautifully, but my monthly token bill was starting to look like a phone number. Every time my agent read a git diff, examined a database structure, or ran a deep directory search, it dumped thousands of lines of raw text into the conversation history. Since coding agents operate in multi-turn loops, reading this accumulated "context bloat" over and over quickly became incredibly slow and expensive.

That's when I found Headroom AI, an open-source tool designed to shrink agent prompt context by 60% to 95% using content-aware compressors (like Tree-sitter for code ASTs and SmartCrusher for JSON).

It sounded perfect. But when I tried to deploy it as a standard local proxy in my Docker Compose stack, I ran into a wall of TCP connection resets, 401 authentication errors, and broken tool calls.

This is the story of how I resolved those network bottlenecks, bypassed a frustrating GitHub Copilot gateway bug, and wrote a lightweight in-process TypeScript middleware to get Headroom running with 100% stability and massive cost savings.


The Initial Plan (And Why My Sockets Blew Up)

The standard integration path for Headroom is running its container as a local proxy on port 8787. You then configure your coding agent to point its OpenAI and Anthropic base URLs to this proxy.

But in my multi-provider setup—where my agent dynamically switches between Google Gemini, Claude, DeepSeek, and Copilot within the same thread—this naive approach broke instantly.

First, I hit a classic network timing race condition. I configured my agent's container to share its network namespace with the Headroom container (network_mode: "service:agent"). During stack boot-up, Headroom tried to bind to port 8787 before the agent container had fully initialized the shared interface. My agent started up, couldn't reach the proxy on localhost, and fell back to uncompressed direct routes.

Second, the community proxy extensions I tried were too aggressive. They acted like a blunt instrument, hijacking any OpenAI-compatible request on-the-fly. When my agent tried to call DeepSeek, the extension intercepted the request and routed it to Headroom's OpenAI handler, which then forwarded my DeepSeek API key to standard OpenAI's servers. Of course, OpenAI rejected my key, and my session crashed with a wall of 401 errors.


Bypassing the Network: The In-Process Middleware Breakthrough

I realized that routing all actual network traffic through an intermediate proxy was too fragile for a multi-provider setup with complex authentication. I needed to separate the network layer from the compression layer.

That's when I audited Headroom's source code and found a hidden gem: the stateless /v1/compress API.

This endpoint doesn't forward requests or touch API keys. You simply send it a raw JSON array of messages, Headroom compresses them in memory, and returns the optimized array instantly.

So, I wrote a custom TypeScript middleware using my agent's native context hook. Instead of hijacking sockets, the extension intercepts the prompt array in RAM right before sending it, makes a quick local POST request to Headroom's compression endpoint, merges the optimized text back, and lets my agent handle its own secure, direct connections to DeepSeek or Copilot.

Here is the exact TypeScript extension I designed and deployed:

import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { readFile, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { join } from "node:path";

interface HeadroomStats {
  totalRequests: number;
  totalTokensBefore: number;
  totalTokensAfter: number;
  totalTokensSaved: number;
}

const statsFile = join(homedir(), ".pi/agent/headroom_savings.json");

async function loadStats(): Promise<HeadroomStats> {
  try {
    const raw = await readFile(statsFile, "utf-8");
    return JSON.parse(raw);
  } catch {
    return { totalRequests: 0, totalTokensBefore: 0, totalTokensAfter: 0, totalTokensSaved: 0 };
  }
}

async function saveStats(stats: HeadroomStats): Promise<void> {
  try {
    await writeFile(statsFile, JSON.stringify(stats, null, 2), "utf-8");
  } catch (err) {
    console.error("[Headroom Middleware] Failed to write headroom_savings.json:", err);
  }
}

export default function (pi: ExtensionAPI) {
  pi.on("context", async (event, ctx) => {
    const proxyUrl = "http://127.0.0.1:8787";

    // Is the Headroom container online?
    try {
      const readyCheck = await fetch(`${proxyUrl}/readyz`, {
        signal: AbortSignal.timeout(1000) // 1-second timeout for graceful fallback
      });
      if (!readyCheck.ok) return;
    } catch {
      return; // Fall back to direct uncompressed pipeline if proxy is offline
    }

    // Call Headroom's stateless API
    try {
      const response = await fetch(`${proxyUrl}/v1/compress`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          messages: event.messages,
          model: ctx.model?.id || "claude-3-5-sonnet", // Guide compressor optimization rules
        }),
        signal: ctx.signal,
      });

      if (response.ok) {
        const data = await response.json();
        if (data && Array.isArray(data.messages)) {
          const compressedMessages = data.messages;

          // --- Smart Payload Fusion ---
          // We preserve Pi-specific metadata (like entryIds and timestamps)
          // and only update the 'content' field with Headroom's compressed output.
          const finalMessages = event.messages.map((origMsg, idx) => {
            const compMsg = compressedMessages[idx];
            if (compMsg && origMsg.role === compMsg.role) {
              return { ...origMsg, content: compMsg.content };
            }
            return origMsg;
          });

          const tokensBefore = data.tokens_before || 0;
          const tokensAfter = data.tokens_after || 0;
          const tokensSaved = data.tokens_saved || 0;

          if (tokensBefore > 0) {
            const stats = await loadStats();
            stats.totalRequests += 1;
            stats.totalTokensBefore += tokensBefore;
            stats.totalTokensAfter += tokensAfter;
            stats.totalTokensSaved += tokensSaved;
            await saveStats(stats);

            const pct = Math.round((1 - (tokensAfter / tokensBefore)) * 100);
            ctx.ui.setStatus("headroom", `Headroom: -${pct}% tokens`);
          }

          return { messages: finalMessages };
        }
      }
    } catch (err) {
      console.error("[Headroom Middleware] Compression error:", err);
    }
  });

  // A native chat command to check my stats in Discord
  pi.registerCommand("headroom", {
    description: "Displays real-time cumulative token savings from Headroom compression",
    handler: async (args, ctx) => {
      const stats = await loadStats();
      if (stats.totalRequests === 0) {
        ctx.ui.notify("Headroom Report: No compression events recorded yet. Send a long prompt to see savings!", "info");
        return;
      }

      const totalPct = Math.round((1 - (stats.totalTokensAfter / stats.totalTokensBefore)) * 100);
      const usdSaved = ((stats.totalTokensSaved / 1000000) * 3.00).toFixed(4);

      const report = [
        "Cumulative Savings Report — Headroom Middleware",
        "──────────────────────────────────",
        `• Optimized requests: ${stats.totalRequests}`,
        `• Total original tokens: ${stats.totalTokensBefore.toLocaleString()}`,
        `• Compressed tokens sent: ${stats.totalTokensAfter.toLocaleString()}`,
        `• Net token savings: ${stats.totalTokensSaved.toLocaleString()} tokens`,
        `• Average compression efficiency: -${totalPct}% tokens`,
        `• Estimated cost savings: $${usdSaved} USD`,
        "──────────────────────────────────",
        "Data and costs flow clean, boss!"
      ].join("\n");

      ctx.ui.notify(report, "info");
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

By switching to this in-process model, my keys are never sent to intermediate proxies, and my agent communicates using 100% native, verified HTTPS connections.


Aligning the Sockets: Inverting the Network Namespace

To completely resolve the container startup race condition, I inverted the networking parent service in my docker-compose.yml.

Instead of my application starting first and creating the loopback network, I set Headroom as the parent service. It boots up first, sets up the network namespace, binds port 8787, and performs its health check. My agent starts second and simply joins Headroom's pre-stabilized network interface (network_mode: "service:headroom").

version: "3.8"

services:
  headroom:
    image: ghcr.io/chopratejas/headroom:code-slim
    container_name: headroom-proxy
    restart: unless-stopped
    ports:
      - "127.0.0.1:8787:8787" # Secure localhost mapping
    networks:
      - agent-net
    volumes:
      - /opt/agent/state/headroom:/home/nonroot/.headroom
    environment:
      - HEADROOM_HOST=0.0.0.0
      - HEADROOM_PORT=8787
      - PYTHONUNBUFFERED=1
    healthcheck:
      test: ["CMD", "python3", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8787/readyz', timeout=2)"]
      interval: 3s
      timeout: 2s
      retries: 5
      start_period: 2s

  agent:
    image: custom-agent:latest
    container_name: agent-bot
    restart: unless-stopped
    network_mode: "service:headroom" # Attaches to headroom's loopback namespace
    depends_on:
      headroom:
        condition: service_healthy # Wait until port 8787 is fully open and listening
Enter fullscreen mode Exit fullscreen mode

This simple change guarantees that port 8787 is open and listening from the first millisecond my agent boots, eliminating loopback failures completely.


The Real-World Results

Since deploying this hybrid middleware architecture, my agent has run flawlessly. It has processed complex file edits, long terminal listings, and multi-turn refactoring loops with absolute stability.

Here are the real statistics from my local persistent log file after a recent debugging session:

Cumulative Savings Report — Headroom Middleware
──────────────────────────────────
• Optimized requests: 37
• Total original tokens: 1,280,450
• Compressed tokens sent: 140,849
• Net token savings: 1,139,601 tokens
• Average compression efficiency: -89% tokens
• Estimated cost savings: $3.4188 USD
──────────────────────────────────
Enter fullscreen mode Exit fullscreen mode

During complex operations, Headroom's SmartCrusher compressed massive tool outputs and git files by up to 92%, letting my agent stay well within deep context windows and reducing response latency significantly.


The Enterprise Blueprint: Scaling to Enterprise with Kong, OIDC & Entra ID

This local setup serves as a solid proof of concept. The next phase is scaling this context-compression architecture across our corporate GCP environment to support hundreds of developers.

To achieve this securely and at scale, the architecture will transition into a centralized, highly available cloud-native service:

  1. Kubernetes Orchestration (GKE): Centralize Headroom proxy pods inside standard private Google Kubernetes Engine (GKE) clusters. These pods scale horizontally via standard HPAs and share a distributed cache database utilizing GCP Memorystore (Redis) to synchronize Context-Compressed Retrieval (CCR) hashes across stateless replicas.
  2. API Gateway & Security (Kong with OIDC): To comply with strict data privacy guidelines, the centralized GKE service will be placed behind Kong API Gateway, integrated with OIDC (OpenID Connect) and Entra ID (formerly Azure Active Directory).
  3. Bring-Your-Own-Key (BYOK) Identity Propagation: Kong will validate the developer’s enterprise JWT, propagate their unique developer identity, and allow Headroom to compress prompts on-the-fly while forwarding the individual developer's OAuth credentials securely to upstream providers—ensuring flawless auditability, security compliance, and dynamic access quota controls.

But for this implementation, I will write other article, so if you are interested, you just have to follow me ;)

What's in Your Tech Stack?

Taming context windows is one of the most critical challenges as we move towards highly autonomous agentic workflows.

I'd love to know: what tools or strategies are you using to manage your context size and keep your LLM bills under control? Let me know in the comments below!

Top comments (0)