Bug Description
When using the OpenAI-compatible LLM path (livekit.plugins.openai.llm.LLM, which inherits its streaming _run loop from livekit.agents.inference.llm.LLMStream), if the upstream provider sends a final streaming chunk where chunk.usage is a non-null object but one of its fields (e.g. completion_tokens) is null, the SDK crashes.
The code at livekit-agents/livekit/agents/inference/llm.py only guards on if chunk.usage is not None: before constructing:
usage=llm.CompletionUsage(
completion_tokens=chunk.usage.completion_tokens,
prompt_tokens=chunk.usage.prompt_tokens,
total_tokens=chunk.usage.total_tokens,
...
)
CompletionUsage.completion_tokens is a required int (no Optional), so a null value raises a pydantic ValidationError, which gets wrapped and re-raised as APIConnectionError(retryable=False). Because it's marked non-retryable, this eventually surfaces as an unrecoverable llm_error that terminates the whole AgentSession — killing an in-progress voice call — even though the actual issue is a malformed/partial usage payload, not a real connection failure.
We're hitting this against an OpenAI-compatible proxy in front of a custom model; the proxy occasionally omits completion_tokens in the usage object on stream completion. This is analogous to #5404, which fixed a similar "usage present but incomplete on final chunk" issue for the Google/Gemini plugin — but the equivalent guard was never added to the shared OpenAI-compatible streaming path.
Expected Behavior
A null/missing field inside chunk.usage should not raise an unhandled, non-retryable exception that ends the session. At minimum, missing numeric usage fields should default to 0 (or the chunk should be treated as retryable) rather than crashing.
Reproduction Steps
- Configure
livekit.plugins.openai.LLM against an OpenAI-compatible endpoint (e.g. a proxy in front of a non-OpenAI model).
- Have that endpoint return a streamed response whose final SSE chunk includes a
usage object with completion_tokens: null (rather than omitting usage entirely).
- Observe the
ValidationError for CompletionUsage, wrapped into APIConnectionError(retryable=False), terminating the session.
Operating System
Linux (GKE)
Models Used
OpenAI-compatible proxy in front of a custom-hosted model
Package Versions
livekit==1.1.13
livekit-agents==1.6.5
livekit-plugins-openai==1.6.5
Proposed Solution
if chunk.usage is not None:
usage = chunk.usage
usage_chunk = llm.ChatChunk(
id=chunk.id,
usage=llm.CompletionUsage(
completion_tokens=usage.completion_tokens or 0,
prompt_tokens=usage.prompt_tokens or 0,
prompt_cached_tokens=cached_tokens or 0,
total_tokens=usage.total_tokens or 0,
service_tier=getattr(chunk, "service_tier", None),
),
)
self._event_ch.send_nowait(usage_chunk)
Additional Context
Traceback:
Traceback (most recent call last):
File ".../livekit/agents/inference/llm.py", line 445, in _run
usage=llm.CompletionUsage(
File ".../pydantic/main.py", line 250, in __init__
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 1 validation error for CompletionUsage
completion_tokens
Input should be a valid integer [type=int_type, input_value=None, input_type=NoneType]
livekit.agents._exceptions.APIConnectionError: Connection error.
Bug Description
When using the OpenAI-compatible LLM path (
livekit.plugins.openai.llm.LLM, which inherits its streaming_runloop fromlivekit.agents.inference.llm.LLMStream), if the upstream provider sends a final streaming chunk wherechunk.usageis a non-null object but one of its fields (e.g.completion_tokens) isnull, the SDK crashes.The code at
livekit-agents/livekit/agents/inference/llm.pyonly guards onif chunk.usage is not None:before constructing:CompletionUsage.completion_tokensis a requiredint(noOptional), so anullvalue raises a pydanticValidationError, which gets wrapped and re-raised asAPIConnectionError(retryable=False). Because it's marked non-retryable, this eventually surfaces as an unrecoverablellm_errorthat terminates the wholeAgentSession— killing an in-progress voice call — even though the actual issue is a malformed/partial usage payload, not a real connection failure.We're hitting this against an OpenAI-compatible proxy in front of a custom model; the proxy occasionally omits
completion_tokensin the usage object on stream completion. This is analogous to #5404, which fixed a similar "usage present but incomplete on final chunk" issue for the Google/Gemini plugin — but the equivalent guard was never added to the shared OpenAI-compatible streaming path.Expected Behavior
A
null/missing field insidechunk.usageshould not raise an unhandled, non-retryable exception that ends the session. At minimum, missing numeric usage fields should default to0(or the chunk should be treated as retryable) rather than crashing.Reproduction Steps
livekit.plugins.openai.LLMagainst an OpenAI-compatible endpoint (e.g. a proxy in front of a non-OpenAI model).usageobject withcompletion_tokens: null(rather than omittingusageentirely).ValidationErrorforCompletionUsage, wrapped intoAPIConnectionError(retryable=False), terminating the session.Operating System
Linux (GKE)
Models Used
OpenAI-compatible proxy in front of a custom-hosted model
Package Versions
Proposed Solution
Additional Context
Traceback: