Skip to content

Commit 5393eb2

Browse files
author
Mateusz
committed
fix(uri-params): let model-string URI beat A-leg body and extra_body
ParameterResolutionService now merges config, header, request, uri, session, and connector_forced in that rising-priority order so URI query parameters on the routed model id override duplicate top-level JSON fields and extra_body values while session and connector-forced settings still win. Updated uri-model-parameters.md to document the full chain and reasoning_effort notes. Adjusted unit and integration tests accordingly. Made-with: Cursor
1 parent b1960cc commit 5393eb2

7 files changed

Lines changed: 32 additions & 30 deletions

File tree

docs/user_guide/features/uri-model-parameters.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This feature is particularly useful when you need to:
1818
- **Inline Parameter Specification**: Append parameters to model strings using URI syntax (e.g., `backend:model?temperature=0.5`)
1919
- **Multiple Parameters**: Support for multiple parameters in a single model string (e.g., `?temperature=0.5&reasoning_effort=high`)
2020
- **Hybrid Backend Support**: Apply different parameters to reasoning and execution models independently
21-
- **Clear Precedence**: URI parameters override config and headers but respect interactive session commands
21+
- **Clear Precedence**: URI parameters on the model selector override A-leg JSON body fields, `extra_body`, and config; session commands and connector-forced settings can still override URI
2222
- **Graceful Error Handling**: Invalid parameters are logged but don't break requests
2323

2424
## Configuration
@@ -28,24 +28,26 @@ This feature is particularly useful when you need to:
2828
The following parameters can be specified via URI syntax:
2929

3030
- **temperature**: Controls randomness in model outputs (0.0-2.0)
31-
- **reasoning_effort**: Controls computational effort for reasoning models (low/medium/high)
31+
- **reasoning_effort**: Controls computational effort for reasoning models (`low` / `medium` / `high`; OpenAI Codex backends also support `xhigh` where the upstream API allows it)
3232
- **top_p**: Controls diversity via nucleus sampling (e.g., 0.9)
3333
- **top_k**: Controls diversity by filtering to the K most likely next tokens (e.g., 40)
3434

3535
### Parameter Precedence
3636

3737
Parameters are resolved from multiple sources with the following precedence (highest to lowest):
3838

39-
1. **Interactive Session Commands** (highest priority) - `!/temperature(0.5)`
40-
2. **URI Parameters** - `model?temperature=0.5`
41-
3. **Request Headers** - `X-Temperature: 0.5`
42-
4. **Configuration File** (lowest priority) - `config.yaml`
39+
1. **Connector-forced settings** (backend `extra` / connector policy) — hard overrides from configuration
40+
2. **Interactive session** — session reasoning mode and commands such as `!/temperature(0.5)` (and edit-precision promotions where applicable)
41+
3. **URI parameters** — query string on the routed model id, e.g. `openai-codex:gpt-5.4-mini?reasoning_effort=xhigh`
42+
4. **A-leg request body** — top-level OpenAI-style fields on the inbound request (for example `temperature`, `reasoning_effort`) when they were actually supplied by the client (schema defaults are not treated as overrides)
43+
5. **`extra_body` sampling fields** — same parameter names carried in `extra_body` (lower than top-level body for resolution)
44+
6. **Backend / app configuration** — defaults from `config.yaml` and backend blocks
4345

4446
When the same parameter is specified in multiple sources, the higher priority source wins. This allows you to:
4547

4648
- Set defaults in config files
47-
- Override per-request with URI parameters
48-
- Override dynamically with session commands
49+
- Let the client send common API fields, but **prefer the model string** when you encode tuning in `backend:model?...`
50+
- Override dynamically with session commands (or connector-forced policy when operators require it)
4951

5052
### Debug Logging
5153

@@ -278,7 +280,7 @@ curl ... -d '{"model": "openai:gpt-4?temperature=0.8", ...}'
278280

279281
**Solutions**:
280282

281-
1. Check parameter precedence - session commands override URI parameters
283+
1. Check parameter precedence session and connector-forced settings override URI; URI overrides duplicate fields on the A-leg request body or in `extra_body`
282284
2. Verify parameter name spelling (case-sensitive)
283285
3. Enable debug logging to see parameter resolution
284286
4. Check that the backend supports the parameter
@@ -290,7 +292,7 @@ curl ... -d '{"model": "openai:gpt-4?temperature=0.8", ...}'
290292
**Solutions**:
291293

292294
1. Check parameter ranges (e.g., temperature: 0.0-2.0)
293-
2. Verify parameter format (e.g., reasoning_effort: low/medium/high)
295+
2. Verify parameter format (e.g., `reasoning_effort`: `low` / `medium` / `high`, and `xhigh` on supported Codex routes)
294296
3. Review logs for specific validation errors
295297
4. Consult backend documentation for supported values
296298

src/core/interfaces/uri_parameter_applicator_interface.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ def apply(
3030
Sources and precedence (highest to lowest):
3131
1. Connector-forced settings (from backend config)
3232
2. Session overrides (from commands)
33-
3. Explicit request body fields (only fields present in
33+
3. URI parameters (from the model alias / query string)
34+
4. Explicit A-leg request body fields (only fields present in
3435
``model_fields_set`` on Pydantic requests, so schema defaults do not
3536
steal precedence from URI or lower sources)
36-
4. URI parameters (from the model alias / query string)
37-
5. Request ``extra_body`` (header-like, lower than URI)
37+
5. Request ``extra_body`` (header-like)
3838
6. Backend / app config
3939
4040
Type coercion rules:

src/core/services/parameter_resolution_service.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ class ParameterResolutionService:
136136
authoritative ordered merge:
137137
1. ``connector_forced_params``
138138
2. ``session_params``
139-
3. ``request_params`` (explicit request fields)
140-
4. ``uri_params``
139+
3. ``uri_params`` (model string / routing selector)
140+
4. ``request_params`` (explicit A-leg body fields)
141141
5. ``header_params`` (e.g. ``extra_body`` in the applicator)
142142
6. ``config_params``
143143
@@ -240,8 +240,8 @@ def _resolve_single_parameter(
240240
Precedence order (highest to lowest):
241241
1. connector_forced_params
242242
2. session_params
243-
3. request_params
244-
4. uri_params
243+
3. uri_params
244+
4. request_params
245245
5. header_params
246246
6. config_params
247247
@@ -262,8 +262,8 @@ def _resolve_single_parameter(
262262
sources = [
263263
("config", config_params.get(param_name)),
264264
("header", header_params.get(param_name)),
265-
("uri", uri_params.get(param_name)),
266265
("request", request_params.get(param_name)),
266+
("uri", uri_params.get(param_name)),
267267
("session", session_params.get(param_name)),
268268
("connector_forced", connector_forced_params.get(param_name)),
269269
]

src/core/services/uri_parameter_applicator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ def apply(
4646
Sources and precedence (highest to lowest):
4747
1. Connector-forced settings
4848
2. Session overrides (from commands)
49-
3. Explicit request fields
50-
4. URI parameters
51-
5. Request extra_body/header-like values
49+
3. URI parameters (model string / selector)
50+
4. Explicit A-leg request body fields
51+
5. Request extra_body (header-like)
5252
6. Backend/app config
5353
5454
Type coercion rules:
@@ -249,7 +249,7 @@ def _extract_header_params(
249249
def _extract_request_params(
250250
self, request: ChatRequest, backend_type: str
251251
) -> dict[str, Any]:
252-
"""Extract explicit request fields with higher precedence than URI params.
252+
"""Extract explicit A-leg body fields (lower precedence than URI params).
253253
254254
For Pydantic v2 models, only attributes listed in ``model_fields_set``
255255
count as user-provided. Schema defaults (e.g. ``reasoning_effort="medium"``)

tests/integration/test_uri_parameters_e2e.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ def test_session_overrides_uri(self) -> None:
247247
assert resolved.temperature.source == "session"
248248

249249
def test_full_precedence_chain(self) -> None:
250-
"""Test complete precedence chain: session > uri > header > config."""
250+
"""Test complete precedence chain: session > uri > request > header > config."""
251251
service = ParameterResolutionService()
252252
resolved = service.resolve_parameters(
253253
config_params={"temperature": 0.1},

tests/unit/core/services/test_parameter_resolution_service.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ def test_precedence_session_overrides_all(self, service):
212212
assert result.temperature.value == 0.2
213213
assert result.temperature.source == "session"
214214

215-
def test_precedence_request_overrides_uri_header_and_config(self, service):
216-
"""Explicit request fields should override URI/header/config parameters."""
215+
def test_precedence_uri_overrides_request_header_and_config(self, service):
216+
"""URI parameters should override A-leg request/header/config parameters."""
217217
result = service.resolve_parameters(
218218
config_params={"temperature": 0.8},
219219
header_params={"temperature": 0.6},
@@ -222,8 +222,8 @@ def test_precedence_request_overrides_uri_header_and_config(self, service):
222222
)
223223

224224
assert result.temperature is not None
225-
assert result.temperature.value == 0.3
226-
assert result.temperature.source == "request"
225+
assert result.temperature.value == 0.4
226+
assert result.temperature.source == "uri"
227227

228228
def test_precedence_connector_forced_overrides_everything(self, service):
229229
"""Connector-forced parameters should have the highest precedence."""

tests/unit/core/services/test_uri_parameter_applicator.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def test_edit_precision_promotes_request_sampling_to_session_precedence(
104104

105105
assert result.temperature == pytest.approx(0.1)
106106

107-
def test_explicit_request_field_overrides_uri_defaults(self) -> None:
107+
def test_uri_overrides_explicit_request_field(self) -> None:
108108
backend_type = "test-backend"
109109
config = _make_config(backend_type, extra={"temperature": 0.9})
110110

@@ -122,7 +122,7 @@ def test_explicit_request_field_overrides_uri_defaults(self) -> None:
122122
session=None,
123123
)
124124

125-
assert result.temperature == pytest.approx(0.7)
125+
assert result.temperature == pytest.approx(0.5)
126126

127127
def test_connector_forced_overrides_uri_and_request(self) -> None:
128128
backend_type = "test-backend"
@@ -203,7 +203,7 @@ def test_matches_backend_service_on_simple_fixture(self) -> None:
203203
session,
204204
)
205205

206-
# Session > request > URI > header > config (connector_forced not used)
206+
# Session > URI > request > header > config (connector_forced not used)
207207
assert applicator_result.temperature == 0.2
208208

209209

0 commit comments

Comments
 (0)