Skip to content

Commit 037cd42

Browse files
committed
refactor: Move backend-specific args to --backend_args
Refactors the CLI arguments to move backend-specific options under the `--backend-args` flag. - Removes `--target-header` and `--target-skip-ssl-verify` from the CLI. - Updates the `OpenAIHTTPBackend` to parse `headers` and `verify` from the `backend_args` dictionary. - Renames `verify_ssl` to the protocol-neutral term `verify`. - Updates documentation and tests to reflect the new argument structure.
1 parent 54d13bb commit 037cd42

File tree

8 files changed

+43
-77
lines changed

8 files changed

+43
-77
lines changed

docs/guides/cli.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ These options configure how `guidellm` connects to the system under test.
1919
| Option | Description |
2020
| --- | --- |
2121
| `--target <URL>` | **Required.** The endpoint of the target system, e.g., `http://localhost:8080`. Can also be set with the `GUIDELLM__OPENAI__BASE_URL` environment variable. |
22-
| `--target-header <HEADER>` | A header to send with requests to the target. Can be specified multiple times. Example: `--target-header "Authorization: Bearer my-secret-token"`. |
23-
| `--target-skip-ssl-verify` | A flag to disable SSL certificate verification when connecting to the target. |
2422
| `--backend-type <TYPE>` | The type of backend to use. Defaults to `openai_http`. |
23+
| `--backend-args <JSON>` | A JSON string for backend-specific arguments. For example: `--backend-args '{"headers": {"Authorization": "Bearer my-token"}, "verify": false}'` to pass custom headers and disable certificate verification. |
2524
| `--model <NAME>` | The ID of the model to benchmark within the backend. |
2625

2726
### Data and Request Configuration

docs/guides/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ You can configure the connection to the target system using environment variable
3131
| `GUIDELLM__OPENAI__HEADERS` | A JSON string representing a dictionary of headers to send to the target. These headers will override any default headers. | `export GUIDELLM__OPENAI__HEADERS='{"Authorization": "Bearer my-token"}'` |
3232
| `GUIDELLM__OPENAI__ORGANIZATION` | The OpenAI organization to use for requests. | `export GUIDELLM__OPENAI__ORGANIZATION="org-12345"` |
3333
| `GUIDELLM__OPENAI__PROJECT` | The OpenAI project to use for requests. | `export GUIDELLM__OPENAI__PROJECT="proj-67890"` |
34-
| `GUIDELLM__OPENAI__VERIFY_SSL` | Set to `false` or `0` to disable SSL certificate verification. | `export GUIDELLM__OPENAI__VERIFY_SSL=false` |
34+
| `GUIDELLM__OPENAI__VERIFY` | Set to `false` or `0` to disable certificate verification. | `export GUIDELLM__OPENAI__VERIFY=false` |
3535
| `GUIDELLM__OPENAI__MAX_OUTPUT_TOKENS` | The default maximum number of tokens to request for completions. | `export GUIDELLM__OPENAI__MAX_OUTPUT_TOKENS=2048` |
3636

3737
### General HTTP Settings
@@ -54,5 +54,5 @@ You can also place these variables in a `.env` file in your project's root direc
5454
GUIDELLM__OPENAI__BASE_URL="http://localhost:8080"
5555
GUIDELLM__OPENAI__API_KEY="your-api-key"
5656
GUIDELLM__OPENAI__HEADERS='{"Authorization": "Bearer my-token"}'
57-
GUIDELLM__OPENAI__VERIFY_SSL=false
57+
GUIDELLM__OPENAI__VERIFY=false
5858
```

src/guidellm/__main__.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,6 @@ def benchmark():
8585
"dict with **kwargs."
8686
),
8787
)
88-
@click.option(
89-
"--target-header",
90-
"target_headers",
91-
multiple=True,
92-
help="A header to send to the target, e.g., --target-header 'Authorization: Bearer <token>'. Can be specified multiple times.",
93-
)
94-
@click.option(
95-
"--target-skip-ssl-verify",
96-
is_flag=True,
97-
default=False,
98-
help="Skip SSL certificate verification when sending requests to the target.",
99-
)
10088
@click.option(
10189
"--model",
10290
default=GenerativeTextScenario.get_default("model"),
@@ -261,8 +249,6 @@ def run(
261249
target,
262250
backend_type,
263251
backend_args,
264-
target_headers,
265-
target_skip_ssl_verify,
266252
model,
267253
processor,
268254
processor_args,
@@ -285,21 +271,6 @@ def run(
285271
):
286272
click_ctx = click.get_current_context()
287273

288-
if target_headers:
289-
headers = {}
290-
for header in target_headers:
291-
if ":" not in header:
292-
raise click.BadParameter(
293-
f"Invalid header format: {header}. Expected 'Key: Value'.",
294-
ctx=click_ctx,
295-
param_hint="--target-header",
296-
)
297-
key, value = header.split(":", 1)
298-
headers[key.strip()] = value.strip()
299-
settings.openai.headers = headers
300-
if target_skip_ssl_verify:
301-
settings.openai.verify_ssl = False
302-
303274
overrides = cli_tools.set_if_not_default(
304275
click_ctx,
305276
target=target,

src/guidellm/backend/openai.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def __init__(
9494
extra_query: Optional[dict] = None,
9595
extra_body: Optional[dict] = None,
9696
remove_from_body: Optional[list[str]] = None,
97+
**kwargs,
9798
):
9899
super().__init__(type_="openai_http")
99100
self._target = target or settings.openai.base_url
@@ -127,8 +128,8 @@ def __init__(
127128
if self.project:
128129
default_headers["OpenAI-Project"] = self.project
129130

130-
# User-provided headers from CLI override defaults
131-
user_headers = settings.openai.headers or {}
131+
# User-provided headers from kwargs or settings override defaults
132+
user_headers = kwargs.pop("headers", settings.openai.headers or {})
132133
default_headers.update(user_headers)
133134
self.headers = default_headers
134135

@@ -139,7 +140,7 @@ def __init__(
139140
if follow_redirects is not None
140141
else settings.request_follow_redirects
141142
)
142-
self.verify_ssl = settings.openai.verify_ssl
143+
self.verify = kwargs.pop("verify", settings.openai.verify)
143144
self.max_output_tokens = (
144145
max_output_tokens
145146
if max_output_tokens is not None
@@ -397,7 +398,7 @@ def _get_async_client(self) -> httpx.AsyncClient:
397398
http2=self.http2,
398399
timeout=self.timeout,
399400
follow_redirects=self.follow_redirects,
400-
verify=self.verify_ssl,
401+
verify=self.verify,
401402
)
402403
self._async_client = client
403404
else:

src/guidellm/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class OpenAISettings(BaseModel):
8686
project: Optional[str] = None
8787
base_url: str = "http://localhost:8000"
8888
max_output_tokens: int = 16384
89-
verify_ssl: bool = True
89+
verify: bool = True
9090

9191

9292
class Settings(BaseSettings):

tests/unit/backend/test_openai_backend_custom_configs.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@
77
@pytest.mark.smoke
88
def test_openai_http_backend_default_initialization():
99
backend = OpenAIHTTPBackend()
10-
assert backend.verify_ssl is True
10+
assert backend.verify is True
1111

1212

1313
@pytest.mark.smoke
1414
def test_openai_http_backend_custom_ssl_verification():
15-
settings.openai.verify_ssl = False
16-
backend = OpenAIHTTPBackend()
17-
assert backend.verify_ssl is False
18-
# Reset the setting
19-
settings.openai.verify_ssl = True
15+
backend = OpenAIHTTPBackend(verify=False)
16+
assert backend.verify is False
2017

2118

2219
@pytest.mark.smoke
@@ -30,10 +27,9 @@ def test_openai_http_backend_custom_headers_override():
3027
"Authorization": openshift_token,
3128
"Custom-Header": "Custom-Value",
3229
}
33-
settings.openai.headers = override_headers
3430

3531
# Initialize the backend
36-
backend = OpenAIHTTPBackend()
32+
backend = OpenAIHTTPBackend(headers=override_headers)
3733

3834
# Check that the override headers are used
3935
assert backend.headers["Authorization"] == openshift_token
@@ -43,3 +39,26 @@ def test_openai_http_backend_custom_headers_override():
4339
# Reset the settings
4440
settings.openai.api_key = None
4541
settings.openai.headers = None
42+
43+
44+
@pytest.mark.smoke
45+
def test_openai_http_backend_kwarg_headers_override_settings():
46+
# Set headers via settings (simulating environment variables)
47+
settings.openai.headers = {"Authorization": "Bearer settings-token"}
48+
49+
# Set different headers via kwargs (simulating --backend-args)
50+
override_headers = {
51+
"Authorization": "Bearer kwargs-token",
52+
"Custom-Header": "Custom-Value",
53+
}
54+
55+
# Initialize the backend with kwargs
56+
backend = OpenAIHTTPBackend(headers=override_headers)
57+
58+
# Check that the kwargs headers took precedence
59+
assert backend.headers["Authorization"] == "Bearer kwargs-token"
60+
assert backend.headers["Custom-Header"] == "Custom-Value"
61+
assert len(backend.headers) == 2
62+
63+
# Reset the settings
64+
settings.openai.headers = None

tests/unit/test_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,12 @@ def test_settings_with_env_variables(mocker):
143143
"GUIDELLM__OPENAI__API_KEY": "env_api_key",
144144
"GUIDELLM__TABLE_BORDER_CHAR": "*",
145145
"GUIDELLM__OPENAI__HEADERS": '{"Authorization": "Bearer env-token"}',
146-
"GUIDELLM__OPENAI__VERIFY_SSL": "false",
146+
"GUIDELLM__OPENAI__VERIFY": "false",
147147
},
148148
)
149149
settings = Settings()
150150
assert settings.dataset.preferred_data_columns == ["custom_column"]
151151
assert settings.openai.api_key == "env_api_key"
152152
assert settings.table_border_char == "*"
153153
assert settings.openai.headers == {"Authorization": "Bearer env-token"}
154-
assert settings.openai.verify_ssl is False
154+
assert settings.openai.verify is False

tests/unit/test_main.py

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,19 @@
22
from click.testing import CliRunner
33

44
from guidellm.__main__ import cli
5+
from guidellm.config import settings
56

67

78
@pytest.mark.smoke
8-
def test_benchmark_run_invalid_header_format():
9+
def test_benchmark_run_with_backend_args():
910
runner = CliRunner()
1011
result = runner.invoke(
1112
cli,
1213
[
1314
"benchmark",
1415
"run",
15-
"--target-header",
16-
"invalid-header",
17-
"--target",
18-
"http://localhost:8000",
19-
"--data",
20-
"prompt_tokens=1,output_tokens=1",
21-
"--rate-type",
22-
"constant",
23-
"--rate",
24-
"1",
25-
"--max-requests",
26-
"1",
27-
],
28-
)
29-
assert result.exit_code != 0
30-
assert "Invalid header format" in result.output
31-
32-
33-
@pytest.mark.smoke
34-
def test_benchmark_run_valid_header_format():
35-
runner = CliRunner()
36-
result = runner.invoke(
37-
cli,
38-
[
39-
"benchmark",
40-
"run",
41-
"--target-header",
42-
"Authorization: Bearer my-token",
16+
"--backend-args",
17+
'{"headers": {"Authorization": "Bearer my-token"}, "verify": false}',
4318
"--target",
4419
"http://localhost:8000",
4520
"--data",
@@ -54,4 +29,5 @@ def test_benchmark_run_valid_header_format():
5429
)
5530
# This will fail because it can't connect to the server,
5631
# but it will pass the header parsing, which is what we want to test.
32+
assert result.exit_code != 0
5733
assert "Invalid header format" not in result.output

0 commit comments

Comments
 (0)