Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,16 @@ async def async_response_stream(
try:
# Try to serialize the chunk object
if hasattr(chunk, "model_dump"):
# Pydantic model
chunk_data = chunk.model_dump()
# Pydantic model. Exclude "delta" from serialization since Langflow's
# /response SSE wraps text deltas as {"content": "..."} for every provider but
# openai SDK's event models declare/expect delta to be str so model_dump() will
# emit noisy logs (PydanticSerializationUnexpectedValue) for every chunk. Basically
# just reattach the raw (unvalidated) value afterwards to preserve same shape.
# TODO: replace this with just chunk.model_dump() if langflow's /response SSE matches openai's
# SDK's delta: str schema
chunk_data = chunk.model_dump(exclude={"delta"})
if hasattr(chunk, "delta"):
chunk_data["delta"] = chunk.delta
elif hasattr(chunk, "__dict__"):
chunk_data = chunk.__dict__
else:
Expand Down
34 changes: 34 additions & 0 deletions tests/unit/test_agent_stream_delta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import warnings

from pydantic import BaseModel


class FakeTextDeltaEvent(BaseModel):
"""Mirrors openai's ResponseTextDeltaEvent: `delta` is declared as `str`."""

delta: str
type: str


chunk = FakeTextDeltaEvent.model_construct(
delta={"content": "Hello"}, type="response.output_text.delta"
)


def test_model_dump_without_exclusion_raises_serialization_warning() -> None:
with warnings.catch_warnings(record=True) as recorded:
warnings.simplefilter("always")
chunk.model_dump()

assert recorded, "expected a serialization warning when delta has a type mismatch"


def test_model_dump_excluding_delta_avoids_warning_and_preserves_dict_shape() -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")

chunk_data = chunk.model_dump(exclude={"delta"})
chunk_data["delta"] = chunk.delta

assert chunk_data["delta"] == {"content": "Hello"}
assert chunk_data["type"] == "response.output_text.delta"
Loading