Commit graph

6 commits

Author SHA1 Message Date
Adryan Eka Vandra
4e9348dec4
fix: add anthropic pre-upstream timeouts 2026-04-21 09:48:32 +07:00
Adryan Eka Vandra
b71b659e1b
fix(ci): restore repro harness test in dev installs
Add websockets to the dev extra so the repro harness smoke test can import
its websocket client dependency in the CI test matrix. Also apply ruff
formatting to the files the formatter check was rejecting so the 3.12 lint
job passes.
2026-04-20 22:12:01 +07:00
Adryan Eka Vandra
8bbf3afda0
perf(proxy): gate /debug/tasks stack-depth computation behind query param
Task.get_stack(limit=32) walks coroutine frames synchronously and
measurably stalls the event loop when called for 50+ relay tasks during
a reconnect storm. The /debug/tasks snapshot does not need this by
default — the perf-cheap fields (name, coro_qualname, age, done) are
enough for the common case of "which tasks are alive".

- collect_tasks: add with_stack_depth=False kwarg; default leaves
  stack_depth=None per entry.
- /debug/tasks: read ?stack=true query parameter and forward it. Default
  response is cheap; opt-in explicitly when human-debugging one snapshot.
- Test: verify default entries have stack_depth=None and that
  ?stack=true produces at least one integer depth.
2026-04-20 22:09:24 +07:00
Adryan Eka Vandra
55f59fad77
chore(proxy): announce pre-upstream concurrency; clean dead RequestLog fields and debug helpers
- Log the resolved Anthropic pre-upstream concurrency at startup so
  operators can correlate pre_upstream_wait_ms lines with the configured
  cap. Distinguishes auto-detected vs. explicit vs. unbounded.
- Remove the stage_timings, session_id, and stage_timings_path fields
  from RequestLog. They were declared optional but never populated at
  any of the three RequestLog construction sites — stage timings flow
  exclusively through emit_stage_timings_log (structured log line) and
  Prometheus histograms. Keeping unpopulated fields misled readers.
  Option (A) from the review.
- Inline the one-liner collect_ws_sessions / collect_warmup pass-throughs
  at their two callers in server.py; keep collect_tasks since it has
  real logic. Unit tests were rewritten against the registry's own
  snapshot()/to_dict() serializers.
2026-04-20 22:09:23 +07:00
Adryan Eka Vandra
0e166c60d4
fix(proxy): accept IPv6-mapped IPv4 loopback in debug guard
On Linux dual-stack sockets (IPV6_V6ONLY=0 default), an IPv4 loopback
connection arrives as ::ffff:127.0.0.1 and was 404'd by the literal set
check — silent outage of /debug/* when the proxy binds to :: or 0.0.0.0.

Replace the set-membership check with ipaddress.ip_address(host).is_loopback,
special-casing 'localhost' (not an IP literal) and treating ValueError
(malformed input) as non-loopback. The None sentinel for TestClient / UDS
sockets is preserved.
2026-04-20 22:08:09 +07:00
Adryan Eka Vandra
a4010aaa1b
feat(proxy): add loopback-only debug introspection endpoints
Unit 5 of the Codex-proxy resilience plan. Three always-on
/debug/* endpoints make "what is this process doing right now?"
a single curl away:

- GET /debug/tasks serializes asyncio.all_tasks() to name,
  coro_qualname, stack_depth, done, and age_seconds (derived from
  the WS session registry for Codex relay tasks, null otherwise).
  Sorted by age desc. No frame locals, no request bodies, no
  coroutine args.
- GET /debug/ws-sessions returns WebSocketSessionRegistry.snapshot().
- GET /debug/warmup returns WarmupRegistry.to_dict().

A FastAPI Depends(require_loopback) dependency 404s (not 403) any
non-loopback caller so debug routes stay invisible to external
scanners. Dependency chosen over middleware because the route set
is small and explicit Depends(...) makes the guard visible to
reviewers and easy to override in tests.
2026-04-20 22:01:42 +07:00