Skip to content

fix(inference-gateway): decode compressed middleware responses - #1218

Merged
BrianNewsom merged 5 commits into
mainfrom
fix-igw-content-encoding/brnewsom
Aug 13, 2026
Merged

fix(inference-gateway): decode compressed middleware responses#1218
BrianNewsom merged 5 commits into
mainfrom
fix-igw-content-encoding/brnewsom

Conversation

@BrianNewsom

@BrianNewsom BrianNewsom commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Inference middleware uses the gateway's non-decompressing HTTP session but then parses upstream bytes as JSON or SSE. When a sandbox egress proxy negotiates gzip, that path sees compressed bytes and returns a misleading JSON 502 or an empty event stream. Enable aiohttp decompression only for middleware-aware requests while leaving direct proxy responses byte-transparent.

Changes

  • Set auto_decompress=True on the middleware-aware upstream request; keep the shared session default disabled for direct proxying.
  • Keep SSE responses live-streaming while aiohttp decodes chunks before the existing parser.
  • Report gateway decode/JSON-processing failures without blaming the model backend.
  • Cover real gzip JSON and SSE responses, payload decode errors, middleware response header sanitization, and unchanged direct compressed pass-through.

Scope boundary

  • Delegate supported Content-Encoding handling to aiohttp instead of maintaining gateway-specific codecs.
  • Do not add custom support for chained content encodings or stricter validation beyond aiohttp's behavior.
  • Do not buffer compressed SSE before returning it. A decode failure after streaming begins terminates that stream; guaranteeing a pre-header 502 would require buffering and is intentionally out of scope.
  • Do not change forwarded Accept-Encoding headers or sandbox proxy configuration; the gateway now handles compression negotiated anywhere on the upstream path.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with documentation updates
  • Documentation only
  • Contributor tooling or automation
  • CI, build, or test infrastructure

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Documentation updated for user-visible behavior
  • Documentation not applicable — justification: This corrects internal gateway response handling without changing a public API or user workflow.

Verification

  • Pull request title follows the repository's Conventional Commit format
  • Every commit includes an appropriate Signed-off-by: trailer
  • uv run pre-commit run -a passes, or any blocked checks are identified below
  • Targeted tests pass, or tests are marked not applicable above
  • No secrets, API keys, or credentials are included

Targeted validation:

  • Pre-fix reproduction: uv run --frozen pytest services/core/inference-gateway/tests/unit/test_proxy.py -k 'fetch_proxy_response_decodes_gzip' -v failed both regression cases with the reported JSON 502 and an empty SSE event list.
  • uv run --frozen pytest services/core/inference-gateway/tests/unit -v — 463 passed, 5 skipped.
  • uv run --frozen pre-commit run -a — all hooks passed.
  • make refresh-openapi — passed with no generated changes.
  • Ticket-path E2E: ran the Experimentalist smoke optimizer in an external-only sbx sandbox against this branch's local gateway. HTTP_PROXY/HTTPS_PROXY were set, host.docker.internal was absent from both NO_PROXY and no_proxy, and the run command did not override either bypass list. Gateway chat-completion requests returned 200 rather than the reported non-JSON 502.
  • The E2E reached run.json.status=completed with winner_agent=agent-1 and validation reward=1.0, shape_ok=1.0. One unrelated upstream request stalled with the gateway's existing unbounded timeout; interrupting and rerunning the documented command with the same experiment directory resumed successfully from persisted state.
  • Post-run Experimentalist guard suite: uv run --frozen pytest plugins/nemo-experimentalist/tests/experimentalist/ -k smoke -q — 76 passed, 15 skipped, 321 deselected.

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
@BrianNewsom
BrianNewsom requested review from a team as code owners August 10, 2026 20:38
@github-actions github-actions Bot added the fix label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The inference gateway now enables aiohttp upstream content decoding. Decoded content feeds SSE and JSON processing. Payload-decoding and JSON-processing failures return distinct 502 errors. Tests cover headers, gzip JSON, gzip SSE, and failure paths.

Changes

Upstream response decoding

Layer / File(s) Summary
Proxy decoding and response processing
services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py, services/core/inference-gateway/src/nmp/core/inference_gateway/service.py, services/core/inference-gateway/src/nmp/core/inference_gateway/testing/_loopback.py
fetch_proxy_response enables aiohttp content decoding. SSE parsing remains streaming. Buffered responses use decoded bodies. Payload, JSON, and non-object response errors use inference-middleware-specific 502 details. Documentation describes the response paths.
Response serialization and validation
services/core/inference-gateway/tests/unit/test_proxy.py
Tests verify removal of stale framing and compression headers, gzip JSON and SSE decoding, and distinct 502 responses for payload-decoding and malformed JSON failures.

Suggested reviewers: a2bondar

Sequence Diagram(s)

sequenceDiagram
  participant Upstream
  participant fetch_proxy_response
  participant SSEParser
  participant JSONParser
  Upstream->>fetch_proxy_response: Encoded response
  fetch_proxy_response->>SSEParser: Decoded streaming chunks
  fetch_proxy_response->>JSONParser: Decoded buffered body
  JSONParser-->>fetch_proxy_response: Parsed object or 502 error
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: decoding compressed middleware responses in the inference gateway.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-igw-content-encoding/brnewsom

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py`:
- Around line 486-499: Update the decompression flow around the chunk loop and
final decoder.flush() to track whether the final compressed member completed
before aiohttp can reset decoder state. Require gzip/deflate
ZLibDecompressor.eof and equivalent end-of-stream completion indicators for
Brotli and Zstandard; raise UpstreamContentDecodingError(content_encoding) for
incomplete bodies, while preserving normal decoded and trailing output.
- Around line 519-524: Update _parse_sse_stream and the surrounding
stream_response_result flow so UpstreamContentDecodingError raised lazily by
_decode_content_chunks or _parse_sse_chunks is handled before SSE output begins,
returning the established 502 response instead of propagating after headers are
sent. Add a regression test covering corrupt gzip content in an SSE response and
verify no partial successful response is emitted.
- Around line 458-471: Update _build_content_decoder to parse comma-separated
Content-Encoding values, ignore surrounding whitespace, and construct a decoder
chain in reverse application order so values such as “gzip, br” decode
correctly. Preserve identity/empty handling and unsupported-coding errors, then
add buffered JSON and fragmented SSE coverage for chained encodings.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a10fde9a-7255-4d13-8dad-a0618ccea8ae

📥 Commits

Reviewing files that changed from the base of the PR and between b08e2a6 and 7605d26.

📒 Files selected for processing (4)
  • services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py
  • services/core/inference-gateway/src/nmp/core/inference_gateway/service.py
  • services/core/inference-gateway/src/nmp/core/inference_gateway/testing/_loopback.py
  • services/core/inference-gateway/tests/unit/test_proxy.py

Comment thread services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py Outdated
Comment thread services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py Outdated
Comment thread services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py Outdated
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 32001/40616 78.8% 63.6%
Integration Tests 18553/38542 48.1% 20.8%

Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/core/inference-gateway/tests/unit/test_proxy.py`:
- Around line 1514-1532: Move the header-sanitization assertions from
test_fetch_proxy_response_decodes_chained_json to a stream_response_result test
that exercises re-serialization. Assert that content-length, content-encoding,
and transfer-encoding are absent from the serialized response headers, while
keeping this fetch_proxy_response test focused on chained JSON decoding and
preserving the direct proxy path’s content-encoding behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bf38e0cc-8b97-430a-83c8-f09c0394241e

📥 Commits

Reviewing files that changed from the base of the PR and between 7605d26 and 15f8c21.

📒 Files selected for processing (2)
  • services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py
  • services/core/inference-gateway/tests/unit/test_proxy.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py

Comment thread services/core/inference-gateway/tests/unit/test_proxy.py Outdated
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>
Signed-off-by: Brian Newsom <brnewsom@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py`:
- Line 492: Restore chained Content-Encoding support in the proxy response
handling around auto_decompress=True by explicitly decoding ordered coding
chains or preserving a decoder chain for multi-coding responses, including
combinations such as gzip, deflate. In
services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py
lines 492-492, update the implementation; in
services/core/inference-gateway/tests/unit/test_proxy.py lines 1498-1518, add
coverage for chained JSON responses and fragmented chained SSE responses. Use uv
for Python tooling.
- Around line 519-522: Update the compressed SSE handling in _parse_sse_stream’s
caller so compressed responses are fully buffered and validated before
constructing or returning ResponseResult, while uncompressed SSE remains
live-streamed. Add corrupt compressed-SSE coverage in
services/core/inference-gateway/tests/unit/test_proxy.py:1553-1581 asserting the
gateway returns 502 before any response iterator is exposed; the proxy
implementation change is required at
services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py:519-522.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9ba3af43-8e0c-46e7-9afc-894c63a1481f

📥 Commits

Reviewing files that changed from the base of the PR and between 15f8c21 and 2adce56.

📒 Files selected for processing (4)
  • services/core/inference-gateway/src/nmp/core/inference_gateway/api/proxy.py
  • services/core/inference-gateway/src/nmp/core/inference_gateway/service.py
  • services/core/inference-gateway/src/nmp/core/inference_gateway/testing/_loopback.py
  • services/core/inference-gateway/tests/unit/test_proxy.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • services/core/inference-gateway/src/nmp/core/inference_gateway/service.py
  • services/core/inference-gateway/src/nmp/core/inference_gateway/testing/_loopback.py

@BrianNewsom
BrianNewsom added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 3a54206 Aug 13, 2026
105 of 106 checks passed
@BrianNewsom
BrianNewsom deleted the fix-igw-content-encoding/brnewsom branch August 13, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants