mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-07-22 22:18:15 +00:00
fix(serialization): strip base64 image data from streamed values events (#3631)
The SSE stream's `values` snapshots serialize the full state, including the hide_from_ui human messages that ViewImageMiddleware fills with base64 image payloads. #3535 stripped those from the REST wait/history/state endpoints via serialize_channel_values_for_api, but the streaming path (worker publishes serialize(chunk, mode="values")) still went through serialize_channel_values, so the same base64 leaked to the frontend over SSE. Route values-mode serialization through serialize_channel_values_for_api as well. Non-hidden messages and https image URLs are left untouched.
This commit is contained in:
parent
b5a4d3414b
commit
3e5c76eb0a
@ -133,11 +133,14 @@ def serialize(obj: Any, *, mode: str = "") -> Any:
|
||||
"""Serialize LangChain objects with mode-specific handling.
|
||||
|
||||
* ``messages`` — obj is ``(message_chunk, metadata_dict)``
|
||||
* ``values`` — obj is the full state dict; ``__pregel_*`` keys stripped
|
||||
* ``values`` — obj is the full state dict; ``__pregel_*`` keys stripped and
|
||||
base64 ``data:`` image blocks dropped from hide_from_ui messages
|
||||
* everything else — recursive ``model_dump()`` / ``dict()`` fallback
|
||||
"""
|
||||
if mode == "messages":
|
||||
return serialize_messages_tuple(obj)
|
||||
if mode == "values":
|
||||
return serialize_channel_values(obj) if isinstance(obj, dict) else serialize_lc_object(obj)
|
||||
# ``values`` snapshots stream the full state to the frontend, so they
|
||||
# must drop base64 image payloads the same way the REST endpoints do.
|
||||
return serialize_channel_values_for_api(obj) if isinstance(obj, dict) else serialize_lc_object(obj)
|
||||
return serialize_lc_object(obj)
|
||||
|
||||
@ -328,3 +328,32 @@ def test_serialize_channel_values_for_api_no_messages():
|
||||
|
||||
result = serialize_channel_values_for_api({"title": "empty"})
|
||||
assert result == {"title": "empty"}
|
||||
|
||||
|
||||
def test_serialize_values_mode_strips_base64_from_hidden_messages():
|
||||
"""The SSE stream emits ``values`` snapshots of the full state, so it must
|
||||
strip base64 image data from hide_from_ui messages just like the REST
|
||||
endpoints do — otherwise the same payload leaks over the stream."""
|
||||
import json
|
||||
|
||||
from deerflow.runtime.serialization import serialize
|
||||
|
||||
state = {
|
||||
"messages": [
|
||||
_make_msg(
|
||||
[
|
||||
{"type": "text", "text": "context"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "data:image/png;base64,iVBOR..."},
|
||||
},
|
||||
],
|
||||
hide_from_ui=True,
|
||||
),
|
||||
],
|
||||
}
|
||||
result = serialize(state, mode="values")
|
||||
# the hidden message survives (count/order preserved) but the data: block is gone
|
||||
assert len(result["messages"]) == 1
|
||||
assert "data:image/png;base64" not in json.dumps(result)
|
||||
assert result["messages"][0]["content"] == [{"type": "text", "text": "context"}]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user