Commit graph

1553 commits

Author SHA1 Message Date
Gyeonghun Park
e98c975153 feat(learn): add CLI-based LLM backends for keyless headroom learn
Allow `headroom learn` to use locally installed coding agent CLIs
(claude, gemini, codex) as LLM backends, so subscription users
without raw API keys can run failure analysis.

Priority: --model flag > API key > HEADROOM_LEARN_CLI env var > auto-detect

- Pass prompts via stdin to avoid ARG_MAX limits
- Handle TimeoutExpired, truncate stderr, enrich JSONDecodeError
- Add 31 new tests (48 total), all passing
- Update docs/learn.md with CLI backend documentation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 22:02:06 +09:00
JerrettDavis
217bf16d3c fix: format telemetry warning tests for CI
Rebase the anonymous telemetry transparency warning work onto the latest main branch and format the new telemetry warning tests so the Python 3.12 lint lane passes cleanly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 23:29:56 -05:00
copilot-swe-agent[bot]
de311f7da4 feat: add anonymous telemetry warning across all user-facing paths
- Add is_telemetry_warn_enabled() feature flag (HEADROOM_TELEMETRY_WARN=off
  to suppress at build/pack time) and format_telemetry_notice() helper to beacon.py
- Export new helpers from headroom.telemetry.__init__
- Proxy CLI startup banner now shows Telemetry: ENABLED/DISABLED with opt-out
  instructions when enabled
- Log telemetry status at proxy server startup() so it appears in log stream
- wrap CLI _launch_tool() and the bespoke claude wrap command both call
  _print_telemetry_notice() so users see the notice before the tool launches
- /stats endpoint now includes anon_telemetry_shipping boolean flag
- Dashboard header shows amber Anon Telemetry indicator chip when
  anon_telemetry_shipping is true (theme-matching, with tooltip showing opt-out)
- 30 new tests covering all paths; all 82 tests pass

Agent-Logs-Url: https://github.com/JerrettDavis/headroom/sessions/880a7bb3-3ad9-49f4-a0b6-3ffdde233e48

Co-authored-by: JerrettDavis <2610199+JerrettDavis@users.noreply.github.com>
2026-04-09 23:26:31 -05:00
Tejas Chopra
ed780b837a
Merge pull request #123 from JerrettDavis/feat/otel-support
Add OTEL metrics and Langfuse tracing support
2026-04-09 21:08:15 -07:00
chopratejas
446872ed53 Plugin architecture for headroom learn + live traffic flush
Refactor headroom learn into a plugin architecture where each coding
agent (Claude Code, Codex, Gemini CLI) is a self-contained plugin
with scanner, writer, and detection logic. External plugins can
register via the headroom.learn_plugin entry point.

- Add LearnPlugin ABC (base.py) and plugin registry (registry.py)
- Move scanners from monolithic scanner.py into plugins/ directory
- Extract shared error classification and tool name map (_shared.py)
- Add GeminiScanner for Google Gemini CLI session parsing
- CLI uses dynamic agent detection via registry (no hardcoded choices)
- All existing imports preserved via backwards-compat re-exports
- Wire agent_type through wrap → proxy → TrafficLearner
- Flush learned patterns to correct .md file at proxy shutdown
- Fix shutdown queue drain bug (patterns were lost on exit)
- 97 tests pass (84 existing + 13 new registry/plugin tests)
2026-04-09 20:30:20 -07:00
JerrettDavis
01fc1aaf94 test: fix codex routing response stub
Update the OpenAI Codex routing test double to match the response shape
expected by the handler so the success path is exercised instead of
falling into the generic failure branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 21:50:38 -05:00
JerrettDavis
be6aa14110 feat: expose proxy OTEL metrics and Langfuse status
Wire the proxy's operational metrics facade into the new observability
layer, expand built-in Prometheus export, surface OTEL and Langfuse status
in /stats, and document the split between anonymous telemetry, OTEL metrics,
and Langfuse traces.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 21:20:46 -05:00
JerrettDavis
ebd482c0a0 feat: add OTEL observability core
Add a shared observability layer for OTEL metrics and Langfuse tracing.
Instrument the shared compression pipeline once so SDK and proxy paths can
reuse the same metrics and spans without scattering telemetry concerns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-09 21:20:34 -05:00
Tejas Chopra
e4f72569c7
Merge pull request #109 from JerrettDavis/feat/openclaw-upstream-gateway
feat(openclaw): route configurable gateway providers through headroom
2026-04-09 17:10:50 -07:00
chopratejas
13081f4647 Skip image tests when Pillow not installed (CI fix)
Tests that create PIL images now have @needs_pillow decorator.
Token estimation and tile math tests still run without Pillow.
2026-04-09 16:24:15 -07:00
chopratejas
65e4a35546 Add OCR routing via RapidOCR + measure actual tokens after compression
Transcode technique now runs RapidOCR to extract text from images.
Falls back to full_low if OCR confidence < 70% or no text detected.

Token counting is now done AFTER compression by measuring the actual
output — no hardcoded estimates. OCR text counted by char length,
resized images re-estimated from new dimensions.

RapidOCR added to headroom-ai[image] extra (~15MB ONNX models, ~180ms CPU).
4 new OCR tests (extraction, blank image, confidence threshold, full pipeline).
29 total image compression tests passing.
2026-04-09 16:22:18 -07:00
chopratejas
7984bd2389 Re-enable image compression: tile optimizer + ONNX router, no PyTorch
Image compression pipeline:
1. Tile-boundary alignment (pure math, 0ms) — saves 340-680 tokens/image
   on OpenAI by snapping to 512px tile boundaries
2. ONNX technique router (32MB, 6ms) — classifies query intent
3. ONNX SigLIP image encoder (95MB, 49ms) — analyzes image properties

Total: ~127MB models, ~55ms latency, no PyTorch dependency.
Re-enabled in both Anthropic and OpenAI proxy handlers.
25 tests.
2026-04-09 16:11:00 -07:00
JerrettDavis
37f32a8922 test(openclaw): cover branch routing paths
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-08 23:46:16 -05:00
JerrettDavis
d4f6e3938f fix(proxy): add fast-fail launch settings
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-08 22:03:21 -05:00
JerrettDavis
eb302fa35e test(proxy): cover codex responses subpath aliases
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-08 21:22:05 -05:00
JerrettDavis
2ceceb4024 fix(proxy): alias nested codex responses routes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-08 21:17:24 -05:00
JerrettDavis
0da1a7b4de Add OpenClaw unwrap flow and non-blocking proxy startup 2026-04-08 21:16:32 -05:00
JerrettDavis
8134939efb fix(openclaw): preserve upstream response paths 2026-04-08 21:15:52 -05:00
JerrettDavis
92b7b09f70 fix(openclaw): route codex through headroom proxy 2026-04-08 21:10:49 -05:00
chopratejas
e7008f64d7 Count Strands reasoningContent, image, document, video tokens (#111 follow-up)
reasoningContent: exact counting via count_text() — pure text, no estimation
image: decode with Pillow for (w*h)/750 formula, fallback by byte size
document: ~1500 tokens/page heuristic (3KB/page of PDF)
video: ~1000 tokens/frame heuristic (30KB/frame)

Text content (reasoning, text, toolResult) uses exact tokenization.
Binary content (image, document, video) uses provider formula or
size-based estimates — accurate counting requires content extraction
that only the provider can do.

14 tests covering all Strands content block types.
2026-04-08 17:31:00 -07:00
JerrettDavis
addcfcffcd Merge upstream/main into feat/docker-e2e-wrap-validation
Resolve the PR merge conflict by carrying forward the secure TLS fail-closed rtk download behavior, bring in the latest upstream proxy-handler updates, and fix the OpenClaw linked-install fallback so it copies the required hook-shim directory with regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 23:54:05 -05:00
JerrettDavis
ca728a4d35 fix(ci): harden wrap e2e validation
Make the Docker wrap e2e harness validate live proxy env wiring for Codex and Aider, start a real OpenClaw gateway in-container, and clear the repo-wide Ruff issues that were keeping the Python 3.12 CI job red.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 23:27:31 -05:00
chopratejas
a9aa66e4c6 Fix 19 test failures: missing security attr, nosec inside f-strings, stale test mock
- Initialize self.security = None on HeadroomProxy (enterprise plugin hook)
- Move '# nosec B608' comments outside f-strings in sqlite.py and fts5.py
  (SQLite doesn't understand # as comment, causing OperationalError)
- Add mark_stable_from_messages to _FakeCompressionCache test mocks
- Update token mode freeze assertion to match cache-aware behavior
2026-04-07 18:30:29 -07:00
Tejas Chopra
22dad133e2 fix: eliminate prefix cache busts from frozen count underestimation
Root cause: CompressionCache.compute_frozen_count() stopped at the first
tool_result not in its cache, capping frozen_message_count at 2. Tool
results excluded by content_router (Read/Glob) or skipped (ratio too
high) never entered the cache, so every subsequent message was eligible
for recompression — causing 192 cache busts per session.

Four fixes:
1. Add _stable_hashes set to CompressionCache so excluded/skipped
   tool_results don't block the frozen count walk
2. Fix _estimate_message_tokens to count tool_result content and
   tool_use input fields (were counted as 0 tokens in Anthropic format)
3. Fix streaming handler to include assistant response and
   original_messages in prefix tracker updates (parity with non-streaming)
4. TTL-aware batch recompression: defer first-time compressions within
   the 5-min cache TTL window, batching them at the boundary to trade
   many small busts for one
2026-04-07 17:10:20 -07:00
Tejas Chopra
c2dde5f6b8
Merge pull request #110 from rrubayet321/main
Add security audit reports and update dependency management
2026-04-07 15:09:15 -07:00
chopratejas
feb163d8c8 Fix Bedrock/Vertex auth regression: don't forward api_key for env-auth providers (#105)
The 0.5.18 refactor added api_key forwarding from request headers to
LiteLLM kwargs in all 4 handler methods. This breaks Bedrock (AWS SigV4)
and Vertex AI (Google ADC) which authenticate via env vars, not API keys.
Forwarding a dummy key like sk-ant-dummy overrides AWS credentials.

Fix: skip api_key forwarding for bedrock, vertex_ai, vertex_ai_beta,
and sagemaker providers. Applied to all 4 occurrences.
2026-04-07 14:24:19 -07:00
chopratejas
8f18468f3b Fix Strands SDK content block tokenization (#111)
Strands SDK sends content blocks without a 'type' field:
  {"text": "..."} instead of {"type": "text", "text": "..."}
  {"toolUse": {...}} instead of {"type": "tool_use", ...}
  {"toolResult": {...}} instead of {"type": "tool_result", ...}

The tokenizer's _count_content_parts() only matched on type field,
causing Strands blocks to fall through to json.dumps estimation.
Now explicitly handles Strands text, toolUse, and toolResult formats
with proper recursive counting for nested toolResult content.

8 new tests covering Strands text blocks, tool blocks, and mixed formats.
2026-04-07 14:19:15 -07:00
rrubayet321
2ae71fe44d chore: add nosec B324 annotations to non-cryptographic MD5 usages and update temporary database path to use system temp directory 2026-04-07 13:07:26 +06:00
JerrettDavis
6f701033a1 test: align anthropic cache stability fixtures
Sync the Anthropic cache stability test double with the prefix tracker contract used by the handler.

Format the benchmark scripts that were failing ruff format --check in CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-06 23:26:51 -05:00
JerrettDavis
78163199c4 Merge upstream/main into feat/extended-cache-ttl-metrics 2026-04-06 21:59:46 -05:00
JerrettDavis
a6787556be Add observed cache TTL metrics and dashboard coverage 2026-04-06 21:13:30 -05:00
JerrettDavis
8e4d7759de Harden cache validation reporting and TTL analysis 2026-04-06 20:11:21 -05:00
chopratejas
f4160f9e47 Fix Codex WebSocket HTTP 500: ChatGPT auth routing, correct beta header, HTTP fallback (#71)
Root causes:
- ChatGPT session auth tokens sent to api.openai.com instead of chatgpt.com
- Fallback beta header was responses-api=v1 instead of responses_websockets=2026-02-06

Fixes:
- Detect ChatGPT-Account-ID header and route WS/HTTP to chatgpt.com/backend-api/codex/responses
- Update beta header fallback to match what Codex actually sends
- Add HTTP POST streaming fallback when upstream WS fails (relay SSE over client WS)
- Unwrap response.create envelope in HTTP fallback for correct POST body
- Initialize body before JSON parse to prevent NameError in fallback path
- Fix async test failures: convert asyncio.get_event_loop() to asyncio.run()
2026-04-06 12:48:46 -07:00
JerrettDavis
5ffa77f4a3 Harden anthropic cache-mode replay stability 2026-04-05 16:11:39 -05:00
JerrettDavis
d00c6739e1 Fix CI regressions for cache benchmark work 2026-04-04 22:33:44 -05:00
JerrettDavis
96fd3d9652 Add Anthropic cache-mode delta replay 2026-04-04 22:23:10 -05:00
JerrettDavis
f2a32f9721 Harden Anthropic cache mode stability 2026-04-04 22:06:52 -05:00
JerrettDavis
1206c0ede2 Improve Claude cache simulation accounting 2026-04-04 21:54:45 -05:00
JerrettDavis
09829be0f4 Add memory-conscious Claude session benchmark harness 2026-04-04 21:19:40 -05:00
JerrettDavis
83b730f2b9 Fix CI lint/format failures after proxy mode hardening 2026-04-04 14:42:32 -05:00
JerrettDavis
2625789a28 Harden cache-mode immutability for OpenAI and fix stats mode reporting 2026-04-04 14:36:29 -05:00
JerrettDavis
54419ad8b8 Rebrand proxy modes to token/cache and harden cache-mode stability 2026-04-04 14:32:07 -05:00
JerrettDavis
d78fdfe02d Merge remote-tracking branch 'upstream/main' into fix/anthropic-prefix-cache-stability
# Conflicts:
#	headroom/proxy/handlers/anthropic.py
2026-04-04 13:54:54 -05:00
JerrettDavis
7d02829f02 Harden Anthropic prefix cache stability across proxy and batch paths 2026-04-04 13:45:37 -05:00
JerrettDavis
cd2741cfff test(cli): add branch coverage for wrap openclaw flows 2026-04-03 23:55:14 -05:00
JerrettDavis
61dacc94a1 fix(cli): default wrap openclaw spec to headroom-ai/openclaw 2026-04-03 23:36:23 -05:00
JerrettDavis
490cf15a57 style: apply ruff formatting for wrap openclaw changes 2026-04-03 23:20:15 -05:00
JerrettDavis
3a25ed4571 fix(cli): make wrap openclaw npm-first and idempotent 2026-04-03 23:15:24 -05:00
JerrettDavis
c5e3686c89 feat(cli): add one-command OpenClaw wrap bootstrap 2026-04-03 23:08:13 -05:00
chopratejas
6a8ae297d6 Refactor: extract 7 modules from server.py (Steps 2-4)
server.py: 8778 → 7412 lines (-1366, -15.5%)

Extracted modules:
- cost.py (629 lines): CostTracker, build_prefix_cache_stats, merge_cost_stats
- prometheus_metrics.py (312 lines): PrometheusMetrics
- semantic_cache.py (142 lines): SemanticCache
- rate_limiter.py (101 lines): TokenBucketRateLimiter
- request_logger.py (108 lines): RequestLogger
- helpers.py (195 lines): _read_request_json, constants, lazy loaders
- models.py (199 lines): ProxyConfig, RequestLog, CacheEntry (from Step 1)

All existing imports via headroom.proxy.server continue to work
through re-exports. Updated test patches to target new module paths.

181 tests pass, 0 regressions.
2026-04-03 16:18:08 -07:00