headroom/headroom/telemetry/context.py
Aashish Tamsya 420dc9077b
feat(grok-build): add Grok Build wrap command and MCP integration (#1629)
## Description

Adds first-class Grok Build support to Headroom so Grok CLI sessions can
route through the local proxy for context compression and savings
tracking.

This PR introduces `headroom wrap grok-build` / `headroom unwrap
grok-build`, a `grok_build` provider slice, Grok MCP registrar support,
and install/telemetry wiring so Grok traffic is attributed correctly in
the proxy and dashboard.

Review follow-up (`9368c413`): when users already own
`[model.grok-build]` in `~/.grok/config.toml`, wrap rewrites `base_url`
in that table in place instead of appending a duplicate header (invalid
TOML).

## Type of Change

- [x] New feature (non-breaking change that adds functionality)

## Changes Made

- Added `headroom/providers/grok_build/` with runtime helpers,
reversible `~/.grok/config.toml` injection, and install env builders.
- Added `headroom wrap grok-build` and `headroom unwrap grok-build` CLI
commands.
- Added `GrokRegistrar` for Headroom MCP registration in Grok config.
- Wired `grok_build` into install planner/registry, agent savings,
telemetry, and proxy client detection (`grok/` user agent).
- **Review fix:** rewrite `base_url` inside an existing user-owned
`[model.grok-build]` table in place (`# was: …` metadata).
- Added regression tests + docs (`grok-build.mdx`, `proxy.mdx`) and
CHANGELOG entry.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ pytest -q tests/test_provider_grok_build.py tests/test_mcp_registry/test_grok_registrar.py
============================== 12 passed in 1.13s ==============================
```

See **Screenshots** below for terminal captures (pytest, review-fix
in-place rewrite, proxy `/readyz`, unwrap).

## Real Behavior Proof

- Environment: macOS, Python 3.11.12 venv, feat/grok-build @ `9368c413`,
isolated `GROK_HOME` temp dirs, proxy port 8799
- Exact command / steps: see screenshot evidence (wrap/unwrap, in-place
table rewrite, `/readyz`)
- Observed result: see screenshots — 12 tests pass; single
`[model.grok-build]` table after wrap on pre-existing config; proxy
healthy; unwrap restores backup
- Not tested: Live interactive Grok chat with xAI auth through the proxy

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

Terminal captures from local verification (`9368c413`). Assets hosted on
fork prerelease only — **not** in the source tree.

**1. Pytest — 12 passed (incl. review-fix regression)**

![pytest 12
passed](https://github.com/aashishtamsya/headroom/releases/download/pr-1629-evidence/01-pytest.png)

**2. Review fix — in-place `[model.grok-build]` rewrite (single table,
`# was:` metadata)**

![review fix in-place
rewrite](https://github.com/aashishtamsya/headroom/releases/download/pr-1629-evidence/02-review-fix-in-place.png)

**3. Proxy health — `/readyz` healthy on port 8799**

![proxy readyz
healthy](https://github.com/aashishtamsya/headroom/releases/download/pr-1629-evidence/03-proxy-health.png)

**4. Unwrap — restores pre-wrap backup**

![unwrap restores
backup](https://github.com/aashishtamsya/headroom/releases/download/pr-1629-evidence/04-unwrap.png)

## Additional Notes

Screenshot assets:
https://github.com/aashishtamsya/headroom/releases/tag/pr-1629-evidence
(temporary prerelease; safe to delete after merge).

---------

Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
2026-07-15 20:51:52 +00:00

148 lines
4.5 KiB
Python

"""Deployment context detection for telemetry.
Derives two orthogonal identity fields the beacon reports:
* ``install_mode`` — how the proxy process is deployed
(``persistent`` / ``on_demand`` / ``wrapped`` / ``unknown``).
* ``headroom_stack`` — how Headroom is being invoked
(``proxy``, ``wrap_claude``, ``adapter_ts_openai``, ...).
Both helpers are best-effort and never raise: telemetry is fire-and-forget and
must not break the proxy.
"""
from __future__ import annotations
import logging
import os
import re
from typing import Any
logger = logging.getLogger(__name__)
_KNOWN_WRAP_AGENTS = frozenset(
{
"claude",
"copilot",
"codex",
"aider",
"cursor",
"grok_build",
"omp",
"openclaw",
"opencode",
}
)
# Stack slugs must start with a letter and contain only [a-z0-9_], max 64 chars.
# Applied at every ingress (env var, HTTP header, stats aggregation) so downstream
# sinks (Prometheus labels, OTEL attributes) see a bounded vocabulary.
_STACK_SLUG_RE = re.compile(r"^[a-z][a-z0-9_]{0,63}$")
# Cardinality cap on the per-process requests_by_stack dict. Protects the
# Prometheus scrape, the in-memory counter, and the JSONB telemetry payload
# from unbounded label explosion when clients send arbitrary X-Headroom-Stack
# header values.
MAX_DISTINCT_STACKS = 32
def normalize_stack(raw: str | None) -> str | None:
"""Validate and normalize a stack slug.
Returns the lowercased/stripped slug if it matches ``^[a-z][a-z0-9_]{0,63}$``,
else ``None``. All external stack identifiers (env var, HTTP header, stats
keys) must pass through this function — it is the single chokepoint that
bounds cardinality and rejects garbage before it reaches Prometheus or the
OTEL metrics layer.
"""
if not raw:
return None
slug = raw.strip().lower()
if not _STACK_SLUG_RE.match(slug):
return None
return slug
def _slug_from_agent_type(agent_type: str) -> str:
"""Return ``wrap_<agent>`` for known agents, otherwise ``unknown``."""
agent_type = agent_type.strip().lower()
if agent_type and agent_type in _KNOWN_WRAP_AGENTS:
return f"wrap_{agent_type}"
return "unknown"
def detect_install_mode(port: int) -> str:
"""Classify how the proxy is deployed.
Resolution order:
1. ``HEADROOM_AGENT_TYPE`` env var set → ``wrapped`` (spawned by ``headroom wrap``).
2. A ``DeploymentManifest`` on disk whose port matches ``port`` → ``persistent``.
3. Otherwise → ``on_demand``.
Any failure falls back to ``unknown`` so a broken install subsystem
doesn't silence telemetry.
"""
try:
if os.environ.get("HEADROOM_AGENT_TYPE"):
return "wrapped"
try:
from headroom.install.state import list_manifests
for manifest in list_manifests():
if getattr(manifest, "port", None) == port:
return "persistent"
except Exception:
logger.debug(
"Beacon: manifest lookup failed during install_mode detection",
exc_info=True,
)
return "on_demand"
except Exception:
logger.debug("Beacon: detect_install_mode crashed", exc_info=True)
return "unknown"
def detect_stack(stats: dict[str, Any] | None = None) -> str:
"""Classify how Headroom is being invoked.
Resolution order:
1. ``HEADROOM_STACK`` env var set → use that slug verbatim.
2. ``HEADROOM_AGENT_TYPE`` env var set → ``wrap_<agent>``.
3. ``stats['requests']['by_stack']`` dict populated →
pick the stack with >80% of requests, else ``mixed``.
4. Otherwise → ``proxy``.
Any failure falls back to ``unknown``.
"""
try:
explicit = normalize_stack(os.environ.get("HEADROOM_STACK"))
if explicit:
return explicit
agent_type = os.environ.get("HEADROOM_AGENT_TYPE")
if agent_type:
return _slug_from_agent_type(agent_type)
if stats:
by_stack = (stats.get("requests") or {}).get("by_stack") or {}
if by_stack:
total = sum(by_stack.values())
if total > 0:
dominant, count = max(by_stack.items(), key=lambda kv: kv[1])
if count / total >= 0.8:
return normalize_stack(str(dominant)) or "unknown"
return "mixed"
return "proxy"
except Exception:
logger.debug("Beacon: detect_stack crashed", exc_info=True)
return "unknown"