Commit graph

11 commits

Author SHA1 Message Date
Sergei Baikin
6576ef639c
fix(openclaw-plugin): circuit breaker + per-request timeout for proxy resilience (#639)
## Description

This change adds bounded timeout and circuit-breaker behavior so
OpenClaw can degrade safely when Headroom or the upstream stream stalls,
while returning structured proxy errors instead of hanging.

Closes #638 by improving OpenClaw/proxy resilience when the Headroom
proxy stalls or Anthropic resets a stream. The PR adds proxy-side
handling for `httpx.RemoteProtocolError`, returns structured 502
responses for otherwise unhandled proxy middleware errors, and adds
OpenClaw plugin timeout/circuit-breaker fallback behavior.

## Type of Change

- [x] Bug fix
- [ ] New feature
- [x] Documentation
- [ ] Refactor
- [x] Tests only

## Changes Made

- Added OpenClaw plugin per-request compression timeout and circuit
breaker fallback.
- Cleared timeout timers after successful or failed compression so
successful calls do not leave pending timers.
- Added a focused Vitest regression for timeout cleanup.
- Added `contracts.tools` for `headroom_retrieve` without whole-file
manifest reformatting.
- Added proxy handling for mid-stream `httpx.RemoteProtocolError` and
structured 502 fallback behavior.
- Documented the new OpenClaw resilience configuration fields.

## Testing

- [x] Unit tests
- [x] Integration-style proxy tests
- [x] Typecheck/build
- [ ] Manual testing

### Test Output

```text
cd plugins/openclaw && npm test
Test Files 6 passed (6), Tests 55 passed (55)

cd plugins/openclaw && npm run typecheck
passed

cd plugins/openclaw && npm run build
Build success

UV_SKIP_WHEEL_FILENAME_CHECK=1 uv run --with pytest --with pytest-asyncio --with fastapi --with httpx --with uvicorn --with h2 python -m pytest tests/test_proxy_streaming_resilience.py -q
24 passed in 2.16s
```

## Real Behavior Proof

- Environment: Windows 11, Node/npm from local plugin worktree, Python
3.13.3, focused local worktree for PR #639.
- Exact command / steps: Installed plugin dependencies, ran OpenClaw
plugin tests/typecheck/build, and ran the proxy streaming resilience
suite with required async/FastAPI/httpx extras.
- Observed result: Plugin tests, typecheck, build, and proxy resilience
tests all passed.
- Not tested: Live OpenClaw gateway session in this pass; original
reporter previously verified patched files in a container and OpenClaw
degraded/recovered cleanly.

## Review Readiness

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

---------

Co-authored-by: Sergei Baikin <sergei.baikin@fotograf.de>
Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
Co-authored-by: JD Davis <jd@jds-macbook-air.tail2a279.ts.net>
2026-08-13 11:52:01 -05:00
felixboenkost-droid
6d116b15f1
Harden OpenClaw plugin proxy routing (#1074)
## Description

Hardens the bundled OpenClaw plugin so configured proxy routing is
fail-closed and `autoStart` is opt-in.

Closes: N/A

This follow-up is intentionally separate from the ContentRouter cache
fix because it changes plugin/gateway behavior rather than core
compression routing.

The plugin should not mutate upstream provider routing unless a
configured proxy URL is reachable and looks like Headroom. It should
also avoid unhandled startup promise rejections when proxy startup is
fire-and-forget.

Why this shape:

- `autoStart: false` by default matches deployments where Headroom is
supervised externally, for example by systemd. The plugin should not
silently start or assume ownership of a proxy unless the operator opted
in.
- Provider routing is fail-closed: a configured URL must first respond
like Headroom, not merely expose a generic liveness endpoint. This
prevents accidentally routing model traffic through the wrong local
service.
- `/readyz` is treated as liveness, not identity. Identity comes from
Headroom-shaped stats endpoints (`/v1/retrieve/stats` or `/stats`)
because those are harder for unrelated services to satisfy by accident.
- Startup remains asynchronous, but errors are captured and exposed
instead of becoming unhandled promise rejections.
- This is a separate PR because the core cache fix is about compression
correctness, while this patch is about integration safety around
OpenClaw gateway routing.

## Type of Change

- [x] Bug fix (non-breaking change fixes issue)
- [ ] New feature (non-breaking change adds functionality)
- [ ] Breaking change (fix or feature would cause existing functionality
change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Make proxy `autoStart` opt-in (`default: false`).
- Probe configured `proxyUrl` before applying provider routing.
- Treat `/readyz` as liveness only; require Headroom-shaped
`/v1/retrieve/stats` or `/stats` for identity.
- Observe fire-and-forget startup promise rejection and expose startup
error for callers.
- Isolate proxy-ready listener failures.
- Keep provider routing deferred when no active/probed Headroom proxy
exists.
- Register retrieve tool with explicit `headroom_retrieve` name.
- Extend plugin/unit tests for configured proxy failures, generic
non-Headroom endpoints, path collisions, and routing behavior.

Changed files:

- `plugins/openclaw/README.md`
- `plugins/openclaw/openclaw.plugin.json`
- `plugins/openclaw/src/engine.ts`
- `plugins/openclaw/src/plugin/index.ts`
- `plugins/openclaw/src/proxy-manager.ts`
- `plugins/openclaw/test/engine.test.ts`
- `plugins/openclaw/test/gateway-config.test.ts`
- `plugins/openclaw/test/plugin-runtime-routing.test.ts`
- `plugins/openclaw/test/proxy-manager.test.ts`

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added new functionality
- [x] Manual testing performed

### Test Output

```text
$ npm test

Test Files  6 passed (6)
Tests  74 passed (74)

$ npm run typecheck
tsc --noEmit

$ npm run build
tsup && node prepare-dist.mjs
Build success
```

## Real Behavior Proof

- Environment: local OpenClaw plugin package in the Headroom repo.
- Exact command / steps:
  - Run plugin test suite.
  - Run TypeScript typecheck.
  - Run plugin build.
- Observed result:
  - Tests passed: `74/74`.
  - Typecheck passed.
  - Build passed.
- Not tested:
- Full OpenClaw Gateway integration as part of this standalone PR prep.

## Review Readiness

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

## Checklist

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

## Screenshots (if applicable)

N/A.

## Additional Notes

Checklist items left unchecked intentionally:

- No CHANGELOG update included.
- No extra comments were needed beyond existing code structure.

Co-authored-by: Björn-Christian Bönkost <bjoern@v2202603344248440850.hotsrv.de>
2026-06-22 22:52:59 -05: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
0da1a7b4de Add OpenClaw unwrap flow and non-blocking proxy startup 2026-04-08 21:16:32 -05:00
JerrettDavis
2edff08668 fix(openclaw): normalize engine messages for discord 2026-04-06 22:18:37 -05:00
JerrettDavis
5f075dee81 fix(openclaw): preserve tool call linkage in converted history 2026-04-03 22:49:30 -05:00
JerrettDavis
3e368f13c8 refactor(openclaw): DRY up duplicate code across plugin
- Extract shared defaultLogger constant, use in both ProxyManager and
  HeadroomContextEngine (was duplicated inline in both constructors)
- Extract parseProxyUrl() helper, reused by normalizeAndValidateProxyUrl
  and withDefaultPort (eliminates redundant URL parsing)
- Add test helpers: stubProbeSuccess/NonHeadroom/Unreachable to replace
  6 identical inline fetch mock constructions
- Remove duplicate probe tests from engine.test.ts (already covered in
  proxy-manager.test.ts)
- Clean up unused probeHeadroomProxy import from engine.test.ts

Net: -26 lines, same coverage (28 tests)
2026-04-03 10:30:10 -05:00
JerrettDavis
3e11dc6316 Remove skipped integration test registration 2026-04-02 22:51:13 -05:00
JerrettDavis
205ec54df2 Harden openclaw plugin proxy handling and metadata 2026-04-02 21:27:12 -05:00
chopratejas
a41c7f0c89 OpenClaw plugin fixes, telemetry fix, cost tracker improvement, tokenBudget support
OpenClaw plugin:
- Fix assistant content always returned as array (fixes flatMap crash)
- Clean up debug logging, restore clean plugin entry point
- Add test for text-only assistant round-trip
- Set headroom-ai dependency to npm (not local path)

Telemetry:
- Switch from sb_publishable_ to JWT anon key (PostgREST needs JWT for RLS)
- Switch from upsert to plain INSERT (anon role upsert was failing with 42501)
- Verified: full payload with all JSONB columns writes successfully (201)

Cost tracker:
- Use output_buffer instead of hardcoded 500 for cost estimation in client.py
- CostTracker counterfactual: value removed tokens at avg effective $/token
  (actual_spend / billed_tokens) instead of uncached list price — aligns
  savings with real billing mix (cache reads, writes, uncached)
- Add debug log for /stats summary payload
- Add 3 tests for new cost savings calculation

TypeScript SDK:
- Add tokenBudget param to compress(), HeadroomClient, CompressOptions
- Proxy /v1/compress accepts optional token_budget to override model limit
2026-03-28 23:46:03 -07:00
chopratejas
053c4a06af feat: add OpenClaw ContextEngine plugin (@headroom-ai/openclaw)
- HeadroomContextEngine: implements ContextEngine interface (bootstrap, ingest,
  assemble, compact, afterTurn, prepareSubagentSpawn, dispose)
- ProxyManager: auto-detects running proxy or spawns one as child process,
  health checks, restart on crash, graceful shutdown
- AgentMessage ↔ OpenAI format conversion (user, assistant/tool_use, toolResult)
- headroom_retrieve CCR tool: agent can retrieve original uncompressed content
- Plugin manifest (openclaw.plugin.json) with config schema
- 11 tests (6 unit + 5 integration), all passing
- Integration tested: 100 servers compressed, 3,735 tokens saved (82%)
2026-03-28 13:43:40 -07:00