106 lines
4.3 KiB
Python
106 lines
4.3 KiB
Python
"""Unit tests for the workflow-level ``_apply_request_model_override`` helper.
|
|
|
|
Mirrors the shape of :mod:`tests.runtime.test_reasoning_effort_validation`
|
|
in scope (workflow-internal helper, no DBOS / no FastAPI). The helper
|
|
backs the ``/model`` slash command's server-side substitution step:
|
|
the request's optional ``model_override`` becomes the effective
|
|
``LLMConfig.model`` for that single execution while also being
|
|
stashed in ``extra["model_override"]`` so the harness path can
|
|
forward it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from omnigent.runtime.workflow import _apply_request_model_override
|
|
from omnigent.spec.types import LLMConfig
|
|
|
|
|
|
def test_apply_request_model_override_none_returns_input_unchanged() -> None:
|
|
"""Passing ``None`` is a no-op — the spec's model and extra survive.
|
|
|
|
Critical to the design contract: when no per-request override
|
|
is set, the agent spec's configured model wins. Any leak here
|
|
would silently swap models on every request.
|
|
"""
|
|
original = LLMConfig(
|
|
model="databricks-gpt-5-4",
|
|
extra={"temperature": 0.5},
|
|
)
|
|
result = _apply_request_model_override(original, None)
|
|
# The returned value is functionally identical to the input.
|
|
assert result.model == "databricks-gpt-5-4"
|
|
assert result.extra == {"temperature": 0.5}
|
|
# ``extra["model_override"]`` is absent — downstream harness
|
|
# propagation must distinguish "no override" from "override
|
|
# set to the spec's own model".
|
|
assert "model_override" not in result.extra
|
|
|
|
|
|
def test_apply_request_model_override_substitutes_model_field() -> None:
|
|
"""A non-None override becomes the effective ``llm_config.model``.
|
|
|
|
The harness subprocess reads ``llm_config.model`` when calling
|
|
the LLM; substituting it here is what makes the override
|
|
actually take effect on the wire.
|
|
"""
|
|
original = LLMConfig(
|
|
model="databricks-gpt-5-4",
|
|
extra={"temperature": 0.5},
|
|
)
|
|
result = _apply_request_model_override(original, "openai/gpt-5.4-mini")
|
|
assert result.model == "openai/gpt-5.4-mini"
|
|
|
|
|
|
def test_apply_request_model_override_stashes_override_in_extra() -> None:
|
|
"""The override is also recorded in ``extra["model_override"]``.
|
|
|
|
The harness path can't distinguish "spec default model" from
|
|
"user-set override" by looking at ``llm_config.model`` alone —
|
|
both produce the same string after substitution. ``extra`` is
|
|
the unambiguous signal that ``the harness HTTP client`` reads to
|
|
decide whether to emit ``body["model_override"]`` on the wire.
|
|
"""
|
|
original = LLMConfig(model="databricks-gpt-5-4", extra={})
|
|
result = _apply_request_model_override(original, "openai/gpt-5.4-mini")
|
|
assert result.extra["model_override"] == "openai/gpt-5.4-mini"
|
|
|
|
|
|
def test_apply_request_model_override_preserves_other_extra_keys() -> None:
|
|
"""Existing ``extra`` keys (temperature, reasoning_effort) survive.
|
|
|
|
``_apply_request_model_override`` runs AFTER
|
|
``_apply_request_reasoning``, so any reasoning_effort the
|
|
request layered in must still be present after the model
|
|
substitution. If this regresses, ``/effort`` and ``/model``
|
|
silently fight when used together.
|
|
"""
|
|
original = LLMConfig(
|
|
model="databricks-gpt-5-4",
|
|
extra={"temperature": 0.5, "reasoning_effort": "high"},
|
|
)
|
|
result = _apply_request_model_override(original, "openai/gpt-5.4-mini")
|
|
# Pre-existing keys still there.
|
|
assert result.extra["temperature"] == 0.5
|
|
assert result.extra["reasoning_effort"] == "high"
|
|
# New key added on top.
|
|
assert result.extra["model_override"] == "openai/gpt-5.4-mini"
|
|
|
|
|
|
def test_apply_request_model_override_does_not_mutate_input() -> None:
|
|
"""The helper returns a new LLMConfig — the input is untouched.
|
|
|
|
Mirrors ``_apply_request_reasoning``'s contract. Mutation of
|
|
the agent's cached ``spec.llm`` would leak the override into
|
|
every subsequent request hitting the same spec, which is
|
|
exactly the bug ``LLMConfig``'s frozen-by-convention contract
|
|
exists to prevent.
|
|
"""
|
|
original = LLMConfig(
|
|
model="databricks-gpt-5-4",
|
|
extra={"temperature": 0.5},
|
|
)
|
|
_apply_request_model_override(original, "openai/gpt-5.4-mini")
|
|
# Original is unchanged after the call.
|
|
assert original.model == "databricks-gpt-5-4"
|
|
assert original.extra == {"temperature": 0.5}
|