Skip to content

feat: forward x headers#30

Merged
m-misiura merged 1 commit into
trustyai-explainability:developfrom
m-misiura:forward-headers
May 1, 2026
Merged

feat: forward x headers#30
m-misiura merged 1 commit into
trustyai-explainability:developfrom
m-misiura:forward-headers

Conversation

@m-misiura

@m-misiura m-misiura commented Mar 18, 2026

Copy link
Copy Markdown
Collaborator

Description

Implemented:

  • All X-* headers from incoming requests are automatically forwarded to outgoing LLM calls (excluding infrastructure headers from proxies)
  • Authorization header is only forwarded to models that need runtime auth (no api_key_env_var and no hardcoded key)
  • Auth tokens are redacted from server logs

Image to test: quay.io/rh-ee-mmisiura/nemo-guardrails:forward-headers_v6

This PR is intended to address the following JIRA

@m-misiura m-misiura force-pushed the forward-headers branch 2 times, most recently from 94dc3d5 to 992ddc2 Compare March 25, 2026 15:34

def test_no_headers_returns_none():
api_request_headers_var.set(None)
assert get_extra_headers_from_request() is None

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment on lines +47 to +48
assert result == {
"Authorization": "Bearer llm-key",

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment thread tests/test_header_forwarding.py Fixed
Comment thread tests/test_header_forwarding.py Fixed
},
}
result = _redact_invocation_params(params)
assert result["extra_headers"]["Authorization"] == "Bearer [REDACTED]"

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Comment thread tests/test_llmrails.py Fixed
Comment thread tests/test_llmrails.py Fixed
Comment thread tests/test_llmrails.py Fixed
Comment thread tests/test_llmrails.py Fixed
Comment thread tests/test_llmrails.py Fixed
@codecov-commenter

codecov-commenter commented Mar 25, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 87.69231% with 8 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop@6a3cfd1). Learn more about missing BASE report.

Files with missing lines Patch % Lines
nemoguardrails/library/hallucination/actions.py 0.00% 5 Missing ⚠️
nemoguardrails/actions/llm/utils.py 88.46% 3 Missing ⚠️
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.
Additional details and impacted files
@@            Coverage Diff             @@
##             develop      #30   +/-   ##
==========================================
  Coverage           ?   76.91%           
==========================================
  Files              ?      200           
  Lines              ?    20469           
  Branches           ?        0           
==========================================
  Hits               ?    15743           
  Misses             ?     4726           
  Partials           ?        0           
Flag Coverage Δ
python 76.91% <87.69%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
nemoguardrails/context.py 100.00% <100.00%> (ø)
nemoguardrails/logging/callbacks.py 92.51% <100.00%> (ø)
nemoguardrails/rails/llm/llmrails.py 91.65% <100.00%> (ø)
nemoguardrails/server/api.py 74.95% <100.00%> (ø)
nemoguardrails/actions/llm/utils.py 84.26% <88.46%> (ø)
nemoguardrails/library/hallucination/actions.py 27.11% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@m-misiura m-misiura marked this pull request as ready for review March 25, 2026 16:31
if api_key:
kwargs["api_key"] = api_key
kwargs["openai_api_key"] = api_key
elif "api_key" not in kwargs:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think we need to deal with the case where the header forwarding fails or no auth header is provided. IIUC, in these cases the LLM call will just be made with api_key="runtime-provided"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Previously If a model had no API key configured (no api_key_env_var, no api_key in params), LangChain would raise an error at init time and the server would error out.

In this PR, models without a static key get a runtime-provided placeholder so LangChain initialises successfully. Real auth should arrive via forwarded Authorization headers at call time. If no auth header is present, the provider would return 401

For models with a static key: forward_auth=False, so we only forward custom X-* headers (e.g. x-maas-subscription), not Authorization. Providers ignore unknown headers, so this should be harmless

Let me know what you think about this logic

# Generate multiple responses with temperature 1.
# Bind the config parameters to the LLM for this call
llm_with_config = llm.bind(temperature=1.0, n=num_responses)
bind_kwargs = {"temperature": 1.0, "n": num_responses}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Will there be other actions that call .bind() ? If so, it would be nice to have a helper function rather than duplicating this header forwarding

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Sure, added a helper function

Comment thread nemoguardrails/actions/llm/utils.py Outdated
if not request_headers:
return None

_INFRA_PREFIXES = ("x-forwarded", "x-real-", "x-request-id", "x-remote-")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This gets recreated every time we run get_extra_headers_from_request - minor nit but can we make this a global variable ?


def test_forward_auth_false_skips_auth():
_set_headers({"authorization": "Bearer key", "x-authorization": "Bearer key2"})
assert get_extra_headers_from_request(forward_auth=False) is None

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
"""Authorization header (K8s/proxy auth) must never reach the LLM."""
_set_headers({"authorization": "Bearer k8s-token"})
result = get_extra_headers_from_request(forward_auth=True)
assert result is None

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
def test_x_authorization_forwarded_without_authorization():
_set_headers({"x-authorization": "Bearer llm-key"})
result = get_extra_headers_from_request(forward_auth=True)
assert result == {"Authorization": "Bearer llm-key"}

Check notice

Code scanning / Bandit

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code. Note test

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
@m-misiura m-misiura merged commit 15e9757 into trustyai-explainability:develop May 1, 2026
6 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants