Skip to content

Commit 630f409

Browse files
Add response token count logic to Bedrock instrumentation. (#1504)
* Add bedrock token counting. * [MegaLinter] Apply linters fixes * Add bedrock token counting. * Add safeguards when grabbing token counts. * Remove extra None defaults. * Cleanup default None checks. --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 7d5adac commit 630f409

10 files changed

+319
-485
lines changed

newrelic/hooks/external_botocore.py

Lines changed: 207 additions & 53 deletions
Large diffs are not rendered by default.

tests/external_aiobotocore/test_bedrock_chat_completion_converse.py

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from conftest import BOTOCORE_VERSION
1818
from testing_support.fixtures import override_llm_token_callback_settings, reset_core_stats_engine, validate_attributes
1919
from testing_support.ml_testing_utils import (
20-
add_token_count_to_events,
20+
add_token_counts_to_chat_events,
2121
disabled_ai_monitoring_record_content_settings,
2222
disabled_ai_monitoring_settings,
2323
events_sans_content,
@@ -49,6 +49,9 @@
4949
"duration": None, # Response time varies each test run
5050
"request.model": "anthropic.claude-3-sonnet-20240229-v1:0",
5151
"response.model": "anthropic.claude-3-sonnet-20240229-v1:0",
52+
"response.usage.prompt_tokens": 26,
53+
"response.usage.completion_tokens": 100,
54+
"response.usage.total_tokens": 126,
5255
"request.temperature": 0.7,
5356
"request.max_tokens": 100,
5457
"response.choices.finish_reason": "max_tokens",
@@ -70,6 +73,7 @@
7073
"role": "system",
7174
"completion_id": None,
7275
"sequence": 0,
76+
"token_count": 0,
7377
"response.model": "anthropic.claude-3-sonnet-20240229-v1:0",
7478
"vendor": "bedrock",
7579
"ingest_source": "Python",
@@ -88,6 +92,7 @@
8892
"role": "user",
8993
"completion_id": None,
9094
"sequence": 1,
95+
"token_count": 0,
9196
"response.model": "anthropic.claude-3-sonnet-20240229-v1:0",
9297
"vendor": "bedrock",
9398
"ingest_source": "Python",
@@ -106,6 +111,7 @@
106111
"role": "assistant",
107112
"completion_id": None,
108113
"sequence": 2,
114+
"token_count": 0,
109115
"response.model": "anthropic.claude-3-sonnet-20240229-v1:0",
110116
"vendor": "bedrock",
111117
"ingest_source": "Python",
@@ -189,7 +195,7 @@ def _test():
189195
@reset_core_stats_engine()
190196
@override_llm_token_callback_settings(llm_token_count_callback)
191197
def test_bedrock_chat_completion_with_token_count(set_trace_info, exercise_model):
192-
@validate_custom_events(add_token_count_to_events(chat_completion_expected_events))
198+
@validate_custom_events(add_token_counts_to_chat_events(chat_completion_expected_events))
193199
# One summary event, one user message, and one response message from the assistant
194200
@validate_custom_event_count(count=4)
195201
@validate_transaction_metrics(
@@ -476,46 +482,3 @@ def _test():
476482
converse_invalid_model(loop, bedrock_converse_server)
477483

478484
_test()
479-
480-
481-
@reset_core_stats_engine()
482-
@override_llm_token_callback_settings(llm_token_count_callback)
483-
def test_bedrock_chat_completion_error_incorrect_access_key_with_token_count(
484-
monkeypatch, bedrock_converse_server, loop, set_trace_info
485-
):
486-
"""
487-
A request is made to the server with invalid credentials. botocore will reach out to the server and receive an
488-
UnrecognizedClientException as a response. Information from the request will be parsed and reported in customer
489-
events. The error response can also be parsed, and will be included as attributes on the recorded exception.
490-
"""
491-
492-
@validate_custom_events(add_token_count_to_events(chat_completion_invalid_access_key_error_events))
493-
@validate_error_trace_attributes(
494-
_client_error_name,
495-
exact_attrs={
496-
"agent": {},
497-
"intrinsic": {},
498-
"user": {
499-
"http.statusCode": 403,
500-
"error.message": "The security token included in the request is invalid.",
501-
"error.code": "UnrecognizedClientException",
502-
},
503-
},
504-
)
505-
@validate_transaction_metrics(
506-
name="test_bedrock_chat_completion_incorrect_access_key_with_token_count",
507-
scoped_metrics=[("Llm/completion/Bedrock/converse", 1)],
508-
rollup_metrics=[("Llm/completion/Bedrock/converse", 1)],
509-
custom_metrics=[(f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1)],
510-
background_task=True,
511-
)
512-
@background_task(name="test_bedrock_chat_completion_incorrect_access_key_with_token_count")
513-
def _test():
514-
set_trace_info()
515-
add_custom_attribute("llm.conversation_id", "my-awesome-id")
516-
add_custom_attribute("llm.foo", "bar")
517-
add_custom_attribute("non_llm_attr", "python-agent")
518-
519-
converse_incorrect_access_key(loop, bedrock_converse_server, monkeypatch)
520-
521-
_test()

tests/external_aiobotocore/test_bedrock_chat_completion_invoke_model.py

Lines changed: 3 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
)
3535
from testing_support.fixtures import override_llm_token_callback_settings, reset_core_stats_engine, validate_attributes
3636
from testing_support.ml_testing_utils import (
37-
add_token_count_to_events,
37+
add_token_count_streaming_events,
38+
add_token_counts_to_chat_events,
3839
disabled_ai_monitoring_record_content_settings,
3940
disabled_ai_monitoring_settings,
4041
disabled_ai_monitoring_streaming_settings,
@@ -206,7 +207,7 @@ def _test():
206207
@reset_core_stats_engine()
207208
@override_llm_token_callback_settings(llm_token_count_callback)
208209
def test_bedrock_chat_completion_with_token_count(set_trace_info, exercise_model, expected_events, expected_metrics):
209-
@validate_custom_events(add_token_count_to_events(expected_events))
210+
@validate_custom_events(add_token_counts_to_chat_events(add_token_count_streaming_events(expected_events)))
210211
# One summary event, one user message, and one response message from the assistant
211212
@validate_custom_event_count(count=3)
212213
@validate_transaction_metrics(
@@ -455,51 +456,6 @@ def _test():
455456
_test()
456457

457458

458-
@reset_core_stats_engine()
459-
@override_llm_token_callback_settings(llm_token_count_callback)
460-
def test_bedrock_chat_completion_error_incorrect_access_key_with_token(
461-
monkeypatch,
462-
bedrock_server,
463-
exercise_model,
464-
set_trace_info,
465-
expected_invalid_access_key_error_events,
466-
expected_metrics,
467-
):
468-
@validate_custom_events(add_token_count_to_events(expected_invalid_access_key_error_events))
469-
@validate_error_trace_attributes(
470-
_client_error_name,
471-
exact_attrs={
472-
"agent": {},
473-
"intrinsic": {},
474-
"user": {
475-
"http.statusCode": 403,
476-
"error.message": "The security token included in the request is invalid.",
477-
"error.code": "UnrecognizedClientException",
478-
},
479-
},
480-
)
481-
@validate_transaction_metrics(
482-
name="test_bedrock_chat_completion",
483-
scoped_metrics=expected_metrics,
484-
rollup_metrics=expected_metrics,
485-
custom_metrics=[(f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1)],
486-
background_task=True,
487-
)
488-
@background_task(name="test_bedrock_chat_completion")
489-
def _test():
490-
monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
491-
492-
with pytest.raises(_client_error): # not sure where this exception actually comes from
493-
set_trace_info()
494-
add_custom_attribute("llm.conversation_id", "my-awesome-id")
495-
add_custom_attribute("llm.foo", "bar")
496-
add_custom_attribute("non_llm_attr", "python-agent")
497-
498-
exercise_model(prompt="Invalid Token", temperature=0.7, max_tokens=100)
499-
500-
_test()
501-
502-
503459
def invoke_model_malformed_request_body(loop, bedrock_server, response_streaming):
504460
async def _coro():
505461
with pytest.raises(_client_error):
@@ -798,58 +754,6 @@ async def _test():
798754
loop.run_until_complete(_test())
799755

800756

801-
@reset_core_stats_engine()
802-
@override_llm_token_callback_settings(llm_token_count_callback)
803-
@validate_custom_events(add_token_count_to_events(chat_completion_expected_streaming_error_events))
804-
@validate_custom_event_count(count=2)
805-
@validate_error_trace_attributes(
806-
_event_stream_error_name,
807-
exact_attrs={
808-
"agent": {},
809-
"intrinsic": {},
810-
"user": {
811-
"error.message": "Malformed input request, please reformat your input and try again.",
812-
"error.code": "ValidationException",
813-
},
814-
},
815-
forgone_params={"agent": (), "intrinsic": (), "user": ("http.statusCode")},
816-
)
817-
@validate_transaction_metrics(
818-
name="test_bedrock_chat_completion",
819-
scoped_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
820-
rollup_metrics=[("Llm/completion/Bedrock/invoke_model_with_response_stream", 1)],
821-
custom_metrics=[(f"Supportability/Python/ML/Bedrock/{BOTOCORE_VERSION}", 1)],
822-
background_task=True,
823-
)
824-
@background_task(name="test_bedrock_chat_completion")
825-
def test_bedrock_chat_completion_error_streaming_exception_with_token_count(loop, bedrock_server, set_trace_info):
826-
"""
827-
Duplicate of test_bedrock_chat_completion_error_streaming_exception, but with token callback being set.
828-
829-
See the original test for a description of the error case.
830-
"""
831-
832-
async def _test():
833-
with pytest.raises(_event_stream_error):
834-
model = "amazon.titan-text-express-v1"
835-
body = (chat_completion_payload_templates[model] % ("Streaming Exception", 0.7, 100)).encode("utf-8")
836-
837-
set_trace_info()
838-
add_custom_attribute("llm.conversation_id", "my-awesome-id")
839-
add_custom_attribute("llm.foo", "bar")
840-
add_custom_attribute("non_llm_attr", "python-agent")
841-
842-
response = await bedrock_server.invoke_model_with_response_stream(
843-
body=body, modelId=model, accept="application/json", contentType="application/json"
844-
)
845-
846-
body = response.get("body")
847-
async for resp in body:
848-
assert resp
849-
850-
loop.run_until_complete(_test())
851-
852-
853757
def test_bedrock_chat_completion_functions_marked_as_wrapped_for_sdk_compatibility(bedrock_server):
854758
assert bedrock_server._nr_wrapped
855759

tests/external_aiobotocore/test_bedrock_embeddings.py

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
)
2828
from testing_support.fixtures import override_llm_token_callback_settings, reset_core_stats_engine, validate_attributes
2929
from testing_support.ml_testing_utils import (
30-
add_token_count_to_events,
30+
add_token_count_to_embedding_events,
3131
disabled_ai_monitoring_record_content_settings,
3232
disabled_ai_monitoring_settings,
3333
events_sans_content,
@@ -164,7 +164,7 @@ def _test():
164164
@reset_core_stats_engine()
165165
@override_llm_token_callback_settings(llm_token_count_callback)
166166
def test_bedrock_embedding_with_token_count(set_trace_info, exercise_model, expected_events):
167-
@validate_custom_events(add_token_count_to_events(expected_events))
167+
@validate_custom_events(add_token_count_to_embedding_events(expected_events))
168168
@validate_custom_event_count(count=1)
169169
@validate_transaction_metrics(
170170
name="test_bedrock_embedding",
@@ -289,45 +289,6 @@ def _test():
289289
_test()
290290

291291

292-
@reset_core_stats_engine()
293-
@override_llm_token_callback_settings(llm_token_count_callback)
294-
def test_bedrock_embedding_error_incorrect_access_key_with_token_count(
295-
monkeypatch, bedrock_server, exercise_model, set_trace_info, expected_invalid_access_key_error_events
296-
):
297-
@validate_custom_events(add_token_count_to_events(expected_invalid_access_key_error_events))
298-
@validate_error_trace_attributes(
299-
_client_error_name,
300-
exact_attrs={
301-
"agent": {},
302-
"intrinsic": {},
303-
"user": {
304-
"http.statusCode": 403,
305-
"error.message": "The security token included in the request is invalid.",
306-
"error.code": "UnrecognizedClientException",
307-
},
308-
},
309-
)
310-
@validate_transaction_metrics(
311-
name="test_bedrock_embedding",
312-
scoped_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
313-
rollup_metrics=[("Llm/embedding/Bedrock/invoke_model", 1)],
314-
background_task=True,
315-
)
316-
@background_task(name="test_bedrock_embedding")
317-
def _test():
318-
monkeypatch.setattr(bedrock_server._request_signer._credentials, "access_key", "INVALID-ACCESS-KEY")
319-
320-
with pytest.raises(_client_error): # not sure where this exception actually comes from
321-
set_trace_info()
322-
add_custom_attribute("llm.conversation_id", "my-awesome-id")
323-
add_custom_attribute("llm.foo", "bar")
324-
add_custom_attribute("non_llm_attr", "python-agent")
325-
326-
exercise_model(prompt="Invalid Token")
327-
328-
_test()
329-
330-
331292
@reset_core_stats_engine()
332293
@validate_custom_events(embedding_expected_malformed_request_body_events)
333294
@validate_custom_event_count(count=1)

0 commit comments

Comments
 (0)