Skip to content

Commit 0cb2f9f

Browse files
committed
fix(rlsapi_v1): record token metrics when verbose post-processing fails
Signed-off-by: Major Hayden <major@redhat.com>
1 parent 0f4a68c commit 0cb2f9f

2 files changed

Lines changed: 75 additions & 1 deletion

File tree

src/app/endpoints/rlsapi_v1.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ async def infer_endpoint( # pylint: disable=R0914
457457
and infer_request.include_metadata
458458
)
459459

460+
response = None
460461
try:
461462
instructions = _build_instructions(infer_request.context.systeminfo)
462463

@@ -474,7 +475,6 @@ async def infer_endpoint( # pylint: disable=R0914
474475
response = cast(OpenAIResponseObject, response)
475476
response_text = extract_text_from_response_items(response.output)
476477
else:
477-
response = None
478478
response_text = await retrieve_simple_response(
479479
input_source,
480480
instructions,
@@ -483,6 +483,8 @@ async def infer_endpoint( # pylint: disable=R0914
483483
)
484484
inference_time = time.monotonic() - start_time
485485
except _INFER_HANDLED_EXCEPTIONS as error:
486+
if verbose_enabled and response is not None:
487+
extract_token_usage(response.usage, model_id) # type: ignore[arg-type]
486488
_record_inference_failure(
487489
background_tasks,
488490
infer_request,

tests/unit/app/endpoints/test_rlsapi_v1.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
# pylint: disable=protected-access
44
# pylint: disable=unused-argument
5+
# pylint: disable=too-many-lines
56

67
import re
78
from collections.abc import Callable
@@ -752,6 +753,77 @@ async def test_infer_include_metadata_ignored_when_verbose_infer_disabled(
752753
assert response.data.output_tokens is None
753754

754755

756+
def _setup_config_mock(
757+
mocker: MockerFixture,
758+
mock_configuration: AppConfig,
759+
verbose_enabled: bool,
760+
) -> None:
761+
"""Helper to set up configuration mock with verbose setting."""
762+
custom_mock = mocker.Mock()
763+
custom_mock.allow_verbose_infer = verbose_enabled
764+
custom_mock.system_prompt = "You are a helpful assistant."
765+
config_mock = mocker.Mock()
766+
config_mock.inference = mock_configuration.inference
767+
config_mock.customization = custom_mock
768+
mocker.patch("app.endpoints.rlsapi_v1.configuration", config_mock)
769+
770+
771+
async def test_infer_verbose_extract_token_usage_on_text_extraction_failure(
772+
mocker: MockerFixture,
773+
mock_configuration: AppConfig,
774+
mock_auth_resolvers: None,
775+
) -> None:
776+
"""Verify extract_token_usage called in except block when text extraction fails."""
777+
_setup_config_mock(mocker, mock_configuration, verbose_enabled=True)
778+
mock_response = mocker.Mock()
779+
mock_response.output = [_create_mock_response_output(mocker, "Response")]
780+
mock_usage = mocker.Mock()
781+
mock_usage.input_tokens = 50
782+
mock_usage.output_tokens = 25
783+
mock_response.usage = mock_usage
784+
_setup_responses_mock(mocker, mocker.AsyncMock(return_value=mock_response))
785+
mocker.patch(
786+
"app.endpoints.rlsapi_v1.extract_text_from_response_items",
787+
side_effect=RuntimeError("text extraction failed"),
788+
)
789+
mock_extract = mocker.patch("app.endpoints.rlsapi_v1.extract_token_usage")
790+
with pytest.raises(RuntimeError):
791+
await infer_endpoint(
792+
infer_request=RlsapiV1InferRequest(
793+
question="How do I list files?", include_metadata=True
794+
),
795+
request=_create_mock_request(mocker),
796+
background_tasks=_create_mock_background_tasks(mocker),
797+
auth=MOCK_AUTH,
798+
)
799+
mock_extract.assert_called_once()
800+
call_args = mock_extract.call_args
801+
assert call_args[0][0] == mock_usage
802+
assert call_args[0][1] == "openai/gpt-4-turbo"
803+
804+
805+
async def test_infer_non_verbose_no_extract_token_usage_on_failure(
806+
mocker: MockerFixture,
807+
mock_configuration: AppConfig,
808+
mock_auth_resolvers: None,
809+
) -> None:
810+
"""Verify extract_token_usage NOT called in except block for non-verbose."""
811+
_setup_config_mock(mocker, mock_configuration, verbose_enabled=False)
812+
mocker.patch(
813+
"app.endpoints.rlsapi_v1.retrieve_simple_response",
814+
side_effect=RuntimeError("retrieval failed"),
815+
)
816+
mock_extract = mocker.patch("app.endpoints.rlsapi_v1.extract_token_usage")
817+
with pytest.raises(RuntimeError):
818+
await infer_endpoint(
819+
infer_request=RlsapiV1InferRequest(question="How do I list files?"),
820+
request=_create_mock_request(mocker),
821+
background_tasks=_create_mock_background_tasks(mocker),
822+
auth=MOCK_AUTH,
823+
)
824+
mock_extract.assert_not_called()
825+
826+
755827
# --- Test Splunk integration ---
756828

757829

0 commit comments

Comments
 (0)