Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions livekit-agents/livekit/agents/_proto.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def encode_by_name(msg_type: type[_ProtoT], src: Any, **overrides: Any) -> _Prot
"""Build ``msg_type`` by copying same-named entries off ``src``, a mapping or an object.

Only fields the proto declares are read, and a field ``src`` has nothing for is
left unset. Seconds-since-epoch floats fill Timestamp fields.
left unset. Seconds-since-epoch floats fill Timestamp fields, and map values are
stringified: every map these protos declare is ``map<string, string>``.
"""

def lookup(name: str) -> Any:
Expand All @@ -70,6 +71,8 @@ def lookup(name: str) -> Any:
continue
if fd.message_type is not None and fd.message_type.full_name == "google.protobuf.Timestamp":
value = encode_timestamp(value)
elif fd.message_type is not None and fd.message_type.GetOptions().map_entry:
value = {k: str(v) for k, v in value.items()}
kwargs[fd.name] = value
return msg_type(**kwargs)

Expand All @@ -94,7 +97,6 @@ def encode_chat_item(item: ChatItem) -> _pb.ChatContext.ChatItem:
role=ROLES[item.role],
content=content,
metrics=encode_metrics(item.metrics),
extra={k: str(v) for k, v in item.extra.items()},
)
)
elif item.type == "function_call":
Expand Down
16 changes: 15 additions & 1 deletion tests/test_chat_item_proto_coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,14 @@
"llm_node_ttfs": 1.0,
},
),
FunctionCall(id="item_fc", call_id="call-1", name="fn", arguments="{}", created_at=_TS),
FunctionCall(
id="item_fc",
call_id="call-1",
name="fn",
arguments="{}",
extra={"livekit": {"inference_deployment": "d"}},
created_at=_TS,
),
FunctionCallOutput(
id="item_fco",
call_id="call-1",
Expand Down Expand Up @@ -121,6 +128,13 @@ def test_message_content_is_one_entry_per_part() -> None:
assert [c.text for c in msg.content] == ["hello", "bye"]


def test_map_values_are_stringified() -> None:
# extra dicts hold provider payloads and flags, the wire maps only strings
extra = {"livekit": {"inference_deployment": "d"}, "flag": True}
msg = encode_by_name(agent_pb.ChatMessage, {"extra": extra})
assert dict(msg.extra) == {k: str(v) for k, v in extra.items()}


def _saturated(src_type: Any, msg_type: type[Message]) -> Any:
kwargs: dict[str, Any] = {}
for fd in msg_type.DESCRIPTOR.fields:
Expand Down
Loading