Skip to content

Commit 10dbd45

Browse files
committed
test(litellm): Cover streaming, async and usage without details
1 parent 6e17aba commit 10dbd45

1 file changed

Lines changed: 187 additions & 0 deletions

File tree

‎tests/integrations/litellm/test_litellm.py‎

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3958,3 +3958,190 @@ def test_cache_write_token_usage_from_anthropic_shaped_usage(
39583958
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 2600
39593959
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 2048
39603960
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 512
3961+
3962+
3963+
def test_streaming_cached_and_reasoning_token_usage(
3964+
reset_litellm_executor,
3965+
sentry_init,
3966+
capture_events,
3967+
get_model_response,
3968+
server_side_event_chunks,
3969+
streaming_chat_completions_model_response,
3970+
):
3971+
sentry_init(
3972+
integrations=[LiteLLMIntegration()],
3973+
disabled_integrations=[StdlibIntegration],
3974+
traces_sample_rate=1.0,
3975+
stream_gen_ai_spans=False,
3976+
)
3977+
events = capture_events()
3978+
3979+
chunks = streaming_chat_completions_model_response(
3980+
usage=CompletionUsage(prompt_tokens=10, completion_tokens=20, total_tokens=30),
3981+
message_contents=("Tes", "t r", "esp", "ons", "e"),
3982+
)
3983+
3984+
# With stream_options={"include_usage": True}, OpenAI sends usage in a final
3985+
# chunk that has no choices.
3986+
usage_chunk = chunks[-1].model_copy(
3987+
update={
3988+
"choices": [],
3989+
"usage": CompletionUsage(
3990+
prompt_tokens=1200,
3991+
completion_tokens=300,
3992+
total_tokens=1500,
3993+
prompt_tokens_details=PromptTokensDetails(
3994+
cached_tokens=1024, cache_write_tokens=128
3995+
),
3996+
completion_tokens_details=CompletionTokensDetails(reasoning_tokens=256),
3997+
),
3998+
}
3999+
)
4000+
4001+
client = OpenAI(api_key="test-key")
4002+
model_response = get_model_response(
4003+
server_side_event_chunks(
4004+
[*chunks, usage_chunk],
4005+
include_event_type=False,
4006+
),
4007+
request_headers={"X-Stainless-Raw-Response": "true"},
4008+
)
4009+
4010+
with mock.patch.object(
4011+
client.completions._client._client,
4012+
"send",
4013+
return_value=model_response,
4014+
), start_transaction(name="litellm test"):
4015+
response = litellm.completion(
4016+
model="gpt-3.5-turbo",
4017+
messages=[{"role": "user", "content": "Hello!"}],
4018+
client=client,
4019+
stream=True,
4020+
stream_options={"include_usage": True},
4021+
)
4022+
for _ in response:
4023+
pass
4024+
4025+
streaming_handler.executor.shutdown(wait=True)
4026+
4027+
(event,) = events
4028+
(span,) = (
4029+
x
4030+
for x in event["spans"]
4031+
if x["op"] == OP.GEN_AI_CHAT and x["origin"] == "auto.ai.litellm"
4032+
)
4033+
4034+
assert span["data"][SPANDATA.GEN_AI_RESPONSE_STREAMING] is True
4035+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 1200
4036+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 1024
4037+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE] == 128
4038+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 300
4039+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS_REASONING] == 256
4040+
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 1500
4041+
4042+
4043+
@pytest.mark.asyncio(loop_scope="session")
4044+
async def test_async_cached_and_reasoning_token_usage(
4045+
sentry_init,
4046+
capture_events,
4047+
get_model_response,
4048+
nonstreaming_chat_completions_model_response,
4049+
):
4050+
sentry_init(
4051+
integrations=[LiteLLMIntegration()],
4052+
disabled_integrations=[StdlibIntegration],
4053+
traces_sample_rate=1.0,
4054+
stream_gen_ai_spans=False,
4055+
)
4056+
events = capture_events()
4057+
4058+
client = AsyncOpenAI(api_key="test-key")
4059+
model_response = get_model_response(
4060+
nonstreaming_chat_completions_model_response(
4061+
response_id="chatcmpl-test",
4062+
response_model="gpt-3.5-turbo",
4063+
message_content="Test response",
4064+
created=1234567890,
4065+
usage=CompletionUsage(
4066+
prompt_tokens=1200,
4067+
completion_tokens=300,
4068+
total_tokens=1500,
4069+
prompt_tokens_details=PromptTokensDetails(cached_tokens=1024),
4070+
completion_tokens_details=CompletionTokensDetails(reasoning_tokens=256),
4071+
),
4072+
),
4073+
serialize_pydantic=True,
4074+
request_headers={"X-Stainless-Raw-Response": "true"},
4075+
)
4076+
4077+
with mock.patch.object(
4078+
client.completions._client._client,
4079+
"send",
4080+
return_value=model_response,
4081+
), start_transaction(name="litellm test"):
4082+
await litellm.acompletion(
4083+
model="gpt-3.5-turbo",
4084+
messages=[{"role": "user", "content": "Hello!"}],
4085+
client=client,
4086+
)
4087+
4088+
await GLOBAL_LOGGING_WORKER.flush()
4089+
await asyncio.sleep(0.5)
4090+
4091+
(event,) = events
4092+
(span,) = (
4093+
x
4094+
for x in event["spans"]
4095+
if x["op"] == OP.GEN_AI_CHAT and x["origin"] == "auto.ai.litellm"
4096+
)
4097+
4098+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 1200
4099+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED] == 1024
4100+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 300
4101+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS_REASONING] == 256
4102+
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 1500
4103+
4104+
4105+
@pytest.mark.parametrize(
4106+
"usage",
4107+
[
4108+
MockUsage(prompt_tokens=10, completion_tokens=20, total_tokens=30),
4109+
CompletionUsage(
4110+
prompt_tokens=10,
4111+
completion_tokens=20,
4112+
total_tokens=30,
4113+
prompt_tokens_details=None,
4114+
completion_tokens_details=None,
4115+
),
4116+
CompletionUsage(
4117+
prompt_tokens=10,
4118+
completion_tokens=20,
4119+
total_tokens=30,
4120+
prompt_tokens_details=PromptTokensDetails(cached_tokens=None),
4121+
completion_tokens_details=CompletionTokensDetails(reasoning_tokens=None),
4122+
),
4123+
],
4124+
)
4125+
def test_token_usage_without_details(sentry_init, capture_events, usage):
4126+
sentry_init(
4127+
integrations=[LiteLLMIntegration()],
4128+
traces_sample_rate=1.0,
4129+
stream_gen_ai_spans=False,
4130+
)
4131+
events = capture_events()
4132+
4133+
response = MockCompletionResponse(usage=usage)
4134+
kwargs = {"model": "gpt-3.5-turbo", "messages": [], "litellm_params": {}}
4135+
4136+
with start_transaction(name="litellm test"):
4137+
_input_callback(kwargs)
4138+
_success_callback(kwargs, response, datetime.now(), datetime.now())
4139+
4140+
(event,) = events
4141+
(span,) = event["spans"]
4142+
assert span["data"][SPANDATA.GEN_AI_USAGE_INPUT_TOKENS] == 10
4143+
assert span["data"][SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS] == 20
4144+
assert span["data"][SPANDATA.GEN_AI_USAGE_TOTAL_TOKENS] == 30
4145+
assert SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHED not in span["data"]
4146+
assert SPANDATA.GEN_AI_USAGE_INPUT_TOKENS_CACHE_WRITE not in span["data"]
4147+
assert SPANDATA.GEN_AI_USAGE_OUTPUT_TOKENS_REASONING not in span["data"]

0 commit comments

Comments
 (0)