|
25 | 25 | from nemoguardrails.colang.v2_x.lang.colang_ast import Flow |
26 | 26 | from nemoguardrails.colang.v2_x.runtime.flows import InternalEvent, InternalEvents |
27 | 27 | from nemoguardrails.context import ( |
| 28 | + api_request_headers_var, |
| 29 | + get_llm_needs_runtime_auth, |
28 | 30 | llm_call_info_var, |
29 | 31 | llm_response_metadata_var, |
30 | 32 | reasoning_trace_var, |
|
54 | 56 | ] |
55 | 57 |
|
56 | 58 |
|
| 59 | +_INFRA_PREFIXES = ("x-forwarded", "x-real-", "x-request-id", "x-remote-") |
| 60 | + |
| 61 | + |
| 62 | +def get_extra_headers_from_request(forward_auth: bool = True) -> Optional[Dict[str, str]]: |
| 63 | + """Forward X-* headers from the incoming request to the LLM call. |
| 64 | +
|
| 65 | + Excludes proxy/infra headers and the inbound Authorization header (which |
| 66 | + typically carries K8s/proxy auth and must never be forwarded to the LLM). |
| 67 | + When forward_auth is True, forwards X-Authorization as Authorization to |
| 68 | + the LLM provider. |
| 69 | + """ |
| 70 | + request_headers = api_request_headers_var.get() |
| 71 | + if not request_headers: |
| 72 | + return None |
| 73 | + |
| 74 | + extra_headers = {} |
| 75 | + |
| 76 | + for k, v in request_headers.items(): |
| 77 | + lower = k.lower() |
| 78 | + if lower in ("authorization", "x-authorization"): |
| 79 | + continue |
| 80 | + if lower.startswith("x-") and not lower.startswith(_INFRA_PREFIXES): |
| 81 | + extra_headers[k] = v |
| 82 | + |
| 83 | + if forward_auth: |
| 84 | + auth = request_headers.get("x-authorization") |
| 85 | + if auth: |
| 86 | + extra_headers["Authorization"] = auth |
| 87 | + |
| 88 | + return extra_headers or None |
| 89 | + |
| 90 | + |
| 91 | +def get_extra_headers_for_llm(llm: "BaseLanguageModel") -> Dict[str, str]: |
| 92 | + """Return extra_headers dict for an LLM based on its runtime auth needs.""" |
| 93 | + needs_runtime_auth = get_llm_needs_runtime_auth(llm) |
| 94 | + extra_headers = get_extra_headers_from_request(forward_auth=needs_runtime_auth) |
| 95 | + return extra_headers or {} |
| 96 | + |
| 97 | + |
57 | 98 | def _infer_provider_from_module(llm: BaseLanguageModel) -> Optional[str]: |
58 | 99 | """Infer provider name from the LLM's module path. |
59 | 100 |
|
@@ -224,6 +265,15 @@ async def llm_call( |
224 | 265 | llm_params_with_stop = llm_params |
225 | 266 |
|
226 | 267 | filtered_params = _filter_params_for_openai_reasoning_models(llm, llm_params_with_stop) |
| 268 | + |
| 269 | + # Forward X-* headers from the incoming request. Only forward Authorization |
| 270 | + # to models that need runtime auth (i.e. had no valid static API key at init). |
| 271 | + needs_runtime_auth = get_llm_needs_runtime_auth(llm) |
| 272 | + extra_headers = get_extra_headers_from_request(forward_auth=needs_runtime_auth) |
| 273 | + if extra_headers: |
| 274 | + filtered_params = filtered_params.copy() if filtered_params else {} |
| 275 | + filtered_params["extra_headers"] = extra_headers |
| 276 | + |
227 | 277 | generation_llm: Union[BaseLanguageModel, Runnable] = llm.bind(**filtered_params) if filtered_params else llm |
228 | 278 |
|
229 | 279 | if streaming_handler: |
|
0 commit comments