Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions openrag/components/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ def _extract_llm_overrides(self, request: dict):
if llm_override.get("model"):
payload["model"] = llm_override["model"]

base_url = (llm_override.get("base_url") or self._base_url).rstrip("/")
api_key = llm_override.get("api_key") or self._api_key
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}",
}
# Only `model` may be overridden by the client. `base_url` / `api_key`
# are deliberately NOT read from the request: honoring a client-supplied
# endpoint enables SSRF (the server would issue requests to an arbitrary
# host) and would leak the server's API key to that host. The endpoint
# and credentials always come from server configuration.
base_url = self._base_url.rstrip("/")
headers = self.headers

return payload, base_url, headers

Expand Down
33 changes: 19 additions & 14 deletions openrag/components/test_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,39 @@ def test_no_override_uses_defaults(self, llm):
assert base_url == "http://default-llm:8000/v1"
assert headers["Authorization"] == "Bearer default-key"

def test_override_all_fields(self, llm):
def test_model_override_applied(self, llm):
request = {
"model": "openrag-my-partition",
"messages": [{"role": "user", "content": "hello"}],
"stream": False,
"metadata": {
"llm_override": {
"base_url": "http://custom-llm:9000/v1",
"api_key": "custom-key",
"model": "custom-model",
}
},
"metadata": {"llm_override": {"model": "custom-model"}},
}
payload, base_url, headers = llm._extract_llm_overrides(request)

assert payload["model"] == "custom-model"
assert base_url == "http://custom-llm:9000/v1"
assert headers["Authorization"] == "Bearer custom-key"
# Endpoint and credentials always come from server config.
assert base_url == "http://default-llm:8000/v1"
assert headers["Authorization"] == "Bearer default-key"

def test_trailing_slash_stripped_from_base_url(self, llm):
def test_client_base_url_and_api_key_override_ignored(self, llm):
# SSRF / key-exfiltration guard: a client-supplied base_url/api_key
# must never be honored.
request = {
"model": "openrag-my-partition",
"stream": False,
"metadata": {"llm_override": {"base_url": "http://custom:8000/v1///"}},
"metadata": {
"llm_override": {
"base_url": "http://169.254.169.254/latest/meta-data",
"api_key": "attacker-key",
"model": "custom-model",
}
},
}
_, base_url, _ = llm._extract_llm_overrides(request)
payload, base_url, headers = llm._extract_llm_overrides(request)

assert base_url == "http://custom:8000/v1"
assert payload["model"] == "custom-model"
assert base_url == "http://default-llm:8000/v1"
assert headers["Authorization"] == "Bearer default-key"

def test_request_params_forwarded_to_payload(self, llm):
request = {
Expand Down
2 changes: 1 addition & 1 deletion openrag/models/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class OpenAIChatCompletionRequest(BaseModel):
"websearch": False,
"llm_override": None,
},
description="Extra custom parameters. Supports 'llm_override' object with optional 'base_url', 'api_key', and 'model' to override the downstream LLM endpoint.",
description="Extra custom parameters. Supports 'llm_override' object with an optional 'model' to override the downstream model name. The LLM endpoint and credentials are fixed by server configuration and cannot be overridden by the client.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update external API docs to match the model-only override contract.

Line 35 correctly narrows llm_override to model-only, but the public docs snippet in docs/content/docs/documentation/API.mdx still advertises base_url/api_key overrides. Please align that doc to prevent client-side contract drift after this breaking change.

🤖 Prompt for 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.

In `@openrag/models/openai.py` at line 35, The description string in the
openray/models/openai.py file correctly documents that llm_override only
supports model-only overrides, but the external API documentation in
docs/content/docs/documentation/API.mdx still references the old contract
allowing base_url and api_key overrides. Update the API documentation to remove
all mentions of base_url and api_key override capabilities for the llm_override
parameter and align it with the current model-only restriction to maintain
consistency between the code contract and public-facing documentation.

)


Expand Down
Loading