abridge: SessionForward + Convert∘Session composition (PR #122 stage A)#133
Merged
Conversation
Port the abridge SessionForward/AnthropicToOpenAI work from origin/abridge/gateway-tito onto master as the first slice of the #122 split: - SessionForward: forward to session-scoped sidecars (create-on-first- request, path rewrite to /sessions/{id}{path}, delete_session reap). - AnthropicToOpenAI: Anthropic Messages agent over any OpenAI-compatible downstream Handler — protocol translation only, transport-blind, with verbatim assistant replay so session recorders byte-match history. - Forward.handler(): the composition seam — a bound Handler a wrapper can sit on without knowing the transport. - proxy: typed DynamicRoutes protocol for construction-time routes; tunnel 400s on non-object JSON bodies instead of coercing to {}. - sio: RemoteSioError carries the error envelope's status_code, so the tunnel replies with the handler's real status (429/400/503) instead of collapsing every remote failure to 502. (The source branch had this producer half; the port initially took only the consumer.) Review fixes on top of the port (from the pre-PR adversarial review): - _ensure_session accepts any 2xx create response, not just 200. - session_id setter now attaches: assigning an id marks the session ready so the first request doesn't create a fresh session and silently overwrite the assigned id. - Forward.handler() returns a closeable handler and AnthropicToOpenAI delegates aclose() to it, so Proxy.stop reaps the forwarder's httpx pool through the converter composition. - Proxy.session() detects a body failure by catching it, not by probing sys.exc_info() — teardown errors are no longer swallowed for callers that open the session inside an unrelated except block. - delete_session() no longer resurrects a closed pool for one DELETE; it uses a one-shot client, so the documented stop → harvest → reap flow leaks nothing. - Assistant replay memory keys on (id, name, canonical args) per tool call, not ids alone — servers that reuse tool-call ids across turns (TGI's literal "0") no longer corrupt the replayed history. Co-authored-by: FatPigeorz <wjhhhhhha0@gmail.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3 tasks
This was referenced Jul 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First slice of the #122 split (see the coordination comment there for the full stage map). Ports the abridge SessionForward / AnthropicToOpenAI work from
origin/abridge/gateway-titoonto master, plus fixes found in the pre-PR adversarial review.What's in
SessionForward — forward to session-scoped sidecars (the TITO gateway shape): creates the session lazily on first request (or eagerly via
open()), rewrites every inbound path to/sessions/{id}{path},delete_session()reaps. Assigningsession_idattaches to an existing session (no create call).AnthropicToOpenAI — Anthropic Messages agent over ANY OpenAI-compatible downstream
Handler. Pure protocol translation, transport-blind, SDK-free; remembers each tool-calling assistant verbatim and replays it on later turns so a session-recording backend byte-matches history.Forward.handler() — the composition seam: a bound
Handlera wrapper can sit on without knowing the transport. Carriesaclose()soProxy.stopreaps the forwarder's pool through the composition:proxy — typed
DynamicRoutesprotocol for construction-time routes (replaces the duck-typed seam); the tunnel 400s on non-object JSON bodies instead of silently coercing to{}.sio (core, small) —
RemoteSioErrornow carries the error envelope'sstatus_code, so the tunnel replies with the handler's real status (429/400/503) instead of collapsing everything to 502. The source branch had this producer half; the initial port took only the consumer — caught by the adversarial review.Review fixes on top of the port
_ensure_sessionaccepts any 2xx create response, not just 200session_idsetter attach semantics (assigned id no longer silently overwritten by a fresh create)aclose()delegation chain:Proxy(AnthropicToOpenAI(fwd.handler()))no longer leaks the httpx pool pastProxy.stopProxy.session()detects body failure by catching it, notsys.exc_info()— teardown errors aren't swallowed for callers inside an unrelatedexceptblockdelete_session()after close uses a one-shot client instead of resurrecting an unowned pool(id, name, canonical args)per tool call — servers that reuse tool-call ids across turns (TGI's literal"0") no longer corrupt replayed historyDeliberately kept from master:
anthropic_from_openai.py+test_adapter_anthropic.py(they carry #124's aclose work).Verification
uv run pytest tests/ plugins/ -q— 422 passed (13 new tests: session lifecycle, attach, 2xx, aclose chain, tunnel status propagation, teardown-in-except, pool reap, replay id-reuse)uv run ruff check ./uv run pyright— clean / 0 errors🤖 Generated with Claude Code