Skip to content

Commit b736fc1

Browse files
committed
bugfix: 修复ag-ui协议返回message_id为空问题
1 parent 043fcfb commit b736fc1

2 files changed

Lines changed: 40 additions & 21 deletions

File tree

tests/server/a2a/converters/test_event_converter.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -301,9 +301,10 @@ def test_includes_optional_fields(self):
301301
class TestBuildMessageMetadata:
302302
def test_includes_object_type_and_tag(self):
303303
event = _make_event(text="hi", object_type="chat.completion", tag="my_tag")
304-
meta = _build_message_metadata(event)
304+
meta = _build_message_metadata(event, "eff-1")
305305
assert meta[MESSAGE_METADATA_OBJECT_TYPE_KEY] == "chat.completion"
306306
assert meta[MESSAGE_METADATA_TAG_KEY] == "my_tag"
307+
assert meta[MESSAGE_METADATA_RESPONSE_ID_KEY] == "eff-1"
307308

308309

309310
# ---------------------------------------------------------------------------
@@ -337,12 +338,12 @@ def test_does_nothing_without_long_running_ids(self):
337338
class TestBuildMessage:
338339
def test_returns_none_for_empty_parts(self):
339340
event = _make_event(text="hi")
340-
assert _build_message(event, [], Role.agent) is None
341+
assert _build_message(event, [], Role.agent, "e1") is None
341342

342343
def test_returns_message_with_parts(self):
343344
event = _make_event(text="hi", response_id="resp-1")
344345
parts = [A2APart(root=TextPart(text="hi"))]
345-
msg = _build_message(event, parts, Role.agent)
346+
msg = _build_message(event, parts, Role.agent, "resp-1")
346347
assert msg is not None
347348
assert msg.role == Role.agent
348349
assert msg.message_id == "resp-1"
@@ -636,7 +637,7 @@ def test_basic_working(self):
636637
)
637638
event = _make_event(text="hi")
638639
ctx = _make_invocation_context()
639-
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1")
640+
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1", effective_id="m1")
640641
assert result.status.state == TaskState.working
641642

642643
def test_auth_required_for_euc(self):
@@ -650,7 +651,7 @@ def test_auth_required_for_euc(self):
650651
msg = Message(message_id="m1", role=Role.agent, parts=[A2APart(root=dp)])
651652
event = _make_event(function_call=FunctionCall(name=REQUEST_EUC_FUNCTION_CALL_NAME, args={}))
652653
ctx = _make_invocation_context()
653-
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1")
654+
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1", effective_id="m1")
654655
assert result.status.state == TaskState.auth_required
655656

656657
def test_input_required_for_long_running(self):
@@ -664,7 +665,7 @@ def test_input_required_for_long_running(self):
664665
msg = Message(message_id="m1", role=Role.agent, parts=[A2APart(root=dp)])
665666
event = _make_event(function_call=FunctionCall(name="other_tool", args={}))
666667
ctx = _make_invocation_context()
667-
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1")
668+
result = _create_status_update_event(msg, ctx, event, "t1", "ctx1", effective_id="m1")
668669
assert result.status.state == TaskState.input_required
669670

670671

@@ -680,15 +681,19 @@ def test_basic(self):
680681
)
681682
event = _make_event(text="hi", response_id="resp-1")
682683
ctx = _make_invocation_context()
683-
result = _create_artifact_update_event(msg, event, ctx, task_id="t1", context_id="ctx1")
684-
assert result.artifact.artifact_id == "resp-1"
684+
result = _create_artifact_update_event(
685+
msg, event, ctx, task_id="t1", context_id="ctx1", effective_id="m1"
686+
)
687+
assert result.artifact.artifact_id == "m1"
685688
assert result.last_chunk is False
686689

687690
def test_last_chunk(self):
688691
msg = Message(message_id="m1", role=Role.agent, parts=[A2APart(root=TextPart(text="hi"))])
689692
event = _make_event(text="hi")
690693
ctx = _make_invocation_context()
691-
result = _create_artifact_update_event(msg, event, ctx, task_id="t1", context_id="ctx1", last_chunk=True)
694+
result = _create_artifact_update_event(
695+
msg, event, ctx, task_id="t1", context_id="ctx1", last_chunk=True, effective_id="m1"
696+
)
692697
assert result.last_chunk is True
693698
assert result.artifact.artifact_id == ""
694699
assert result.artifact.parts == []

trpc_agent_sdk/server/a2a/converters/_event_converter.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -206,18 +206,18 @@ def _build_context_metadata(event: Event, ctx: InvocationContext) -> Dict[str, A
206206
return metadata
207207

208208

209-
def _build_message_metadata(event: Event) -> Dict[str, Any]:
209+
def _build_message_metadata(event: Event, effective_id: str) -> Dict[str, Any]:
210210
"""Build message/event metadata (object_type, tag, llm_response_id)."""
211211
return {
212212
MESSAGE_METADATA_OBJECT_TYPE_KEY: _infer_message_object_type(event) or "",
213213
MESSAGE_METADATA_TAG_KEY: _infer_message_tag(event),
214-
MESSAGE_METADATA_RESPONSE_ID_KEY: event.response_id or "",
214+
MESSAGE_METADATA_RESPONSE_ID_KEY: effective_id,
215215
}
216216

217217

218-
def _build_event_metadata(event: Event, message: Message, ctx: InvocationContext) -> Dict[str, Any]:
218+
def _build_event_metadata(event: Event, message: Message, ctx: InvocationContext, effective_id: str) -> Dict[str, Any]:
219219
metadata = _build_context_metadata(event, ctx)
220-
msg_meta = _build_message_metadata(event)
220+
msg_meta = _build_message_metadata(event, effective_id)
221221
set_metadata(metadata, MESSAGE_METADATA_OBJECT_TYPE_KEY, msg_meta.get(MESSAGE_METADATA_OBJECT_TYPE_KEY) or "")
222222
set_metadata(metadata, MESSAGE_METADATA_TAG_KEY, msg_meta.get(MESSAGE_METADATA_TAG_KEY) or "")
223223
set_metadata(metadata, MESSAGE_METADATA_RESPONSE_ID_KEY, msg_meta.get(MESSAGE_METADATA_RESPONSE_ID_KEY) or "")
@@ -240,13 +240,21 @@ def _mark_long_running_tools(a2a_parts: List[A2APart], event: Event) -> None:
240240
set_metadata(root.metadata, A2A_DATA_PART_METADATA_IS_LONG_RUNNING_KEY, True)
241241

242242

243-
def _build_message(event: Event, a2a_parts: List[A2APart], role: Role) -> Optional[Message]:
243+
def _effective_response_id(event: Event) -> str:
244+
"""Return ``response_id`` when present, otherwise a new UUID.
245+
246+
Callers that need the same id across multiple locations should invoke this
247+
once and pass the result explicitly.
248+
"""
249+
return event.response_id or str(uuid.uuid4())
250+
251+
252+
def _build_message(event: Event, a2a_parts: List[A2APart], role: Role, effective_id: str) -> Optional[Message]:
244253
"""Assemble an A2A Message from converted parts, or return None if empty."""
245254
if not a2a_parts:
246255
return None
247-
message_id = event.response_id or str(uuid.uuid4())
248-
message = Message(message_id=message_id, role=role, parts=a2a_parts)
249-
msg_meta = _build_message_metadata(event)
256+
message = Message(message_id=effective_id, role=role, parts=a2a_parts)
257+
msg_meta = _build_message_metadata(event, effective_id)
250258
if msg_meta:
251259
message.metadata = msg_meta
252260
return message
@@ -320,7 +328,8 @@ def convert_event_to_a2a_message(
320328
return None
321329

322330
a2a_parts = _collect_parts(event, **rules)
323-
return _build_message(event, a2a_parts, role)
331+
effective_id = _effective_response_id(event)
332+
return _build_message(event, a2a_parts, role, effective_id)
324333

325334

326335
def convert_content_to_a2a_message(
@@ -596,6 +605,7 @@ def _create_status_update_event(
596605
event: Event,
597606
task_id: Optional[str],
598607
context_id: Optional[str],
608+
effective_id: str = "",
599609
) -> TaskStatusUpdateEvent:
600610
status = TaskStatus(state=TaskState.working, message=message, timestamp=_now_iso())
601611

@@ -614,7 +624,7 @@ def _create_status_update_event(
614624
task_id=task_id,
615625
context_id=context_id,
616626
status=status,
617-
metadata=_build_event_metadata(event, message, ctx),
627+
metadata=_build_event_metadata(event, message, ctx, effective_id),
618628
final=False,
619629
)
620630

@@ -626,8 +636,9 @@ def _create_artifact_update_event(
626636
task_id: Optional[str] = None,
627637
context_id: Optional[str] = None,
628638
last_chunk: bool = False,
639+
effective_id: str = "",
629640
) -> TaskArtifactUpdateEvent:
630-
artifact_id = "" if last_chunk else (event.response_id or "")
641+
artifact_id = "" if last_chunk else effective_id
631642
return TaskArtifactUpdateEvent(
632643
task_id=task_id,
633644
context_id=context_id,
@@ -636,7 +647,7 @@ def _create_artifact_update_event(
636647
parts=[] if last_chunk else message.parts,
637648
),
638649
last_chunk=last_chunk,
639-
metadata=_build_event_metadata(event, message, ctx),
650+
metadata=_build_event_metadata(event, message, ctx, effective_id),
640651
)
641652

642653

@@ -674,12 +685,14 @@ def _notify(evt: A2AEvent) -> None:
674685

675686
message = convert_event_to_a2a_message(event, invocation_context)
676687
if message:
688+
effective_id = message.message_id
677689
status_event = _create_status_update_event(
678690
message,
679691
invocation_context,
680692
event,
681693
task_id,
682694
context_id,
695+
effective_id=effective_id,
683696
)
684697
_notify(status_event)
685698

@@ -691,6 +704,7 @@ def _notify(evt: A2AEvent) -> None:
691704
task_id=task_id,
692705
context_id=context_id,
693706
last_chunk=False,
707+
effective_id=effective_id,
694708
)
695709
a2a_events.append(artifact_event)
696710

0 commit comments

Comments
 (0)