diff --git a/src/plyngent/lmproto/openai_compatible/model.py b/src/plyngent/lmproto/openai_compatible/model.py index b682461..831a997 100644 --- a/src/plyngent/lmproto/openai_compatible/model.py +++ b/src/plyngent/lmproto/openai_compatible/model.py @@ -236,8 +236,9 @@ class StreamToolCallDelta(Struct): class DeltaMessage(Struct): role: RoleAssistant | Unset = UNSET - content: str | Unset = UNSET - reasoning_content: str | Unset = UNSET + # Providers often send content=null / reasoning_content=null on partial chunks. + content: str | None | Unset = UNSET + reasoning_content: str | None | Unset = UNSET tool_calls: list[StreamToolCallDelta] | Unset = UNSET diff --git a/tests/test_lmproto/test_sse_parse.py b/tests/test_lmproto/test_sse_parse.py index ea5db12..5a4c692 100644 --- a/tests/test_lmproto/test_sse_parse.py +++ b/tests/test_lmproto/test_sse_parse.py @@ -34,6 +34,19 @@ def test_sse_data_payload_json() -> None: assert chunk.choices[0].delta.content == "hi" +def test_delta_accepts_null_content_and_reasoning() -> None: + """DeepSeek / OpenAI often send content=null on reasoning-only or role chunks.""" + payload = ( + b'{"id":"1","object":"chat.completion.chunk","created":0,"model":"m",' + b'"choices":[{"index":0,"delta":{"role":"assistant","content":null,' + b'"reasoning_content":null},"finish_reason":null}]}' + ) + chunk = msgspec_decode_chunk(payload) + delta = chunk.choices[0].delta + assert delta.content is None + assert delta.reasoning_content is None + + def msgspec_decode_chunk(payload: bytes) -> ChatCompletionChunk: import msgspec