Python: fix: A2AAgent.run() ignores session parameter for context_id propagation#5213
Python: fix: A2AAgent.run() ignores session parameter for context_id propagation#5213LEDazzio01 wants to merge 2 commits intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes Python A2A agent session continuity by ensuring the outbound A2A context_id is derived from the provided AgentSession (or falls back to message metadata / a generated UUID), enabling remote agents to correlate multi-turn conversations.
Changes:
- Propagate
session.session_idinto outbound A2A messages via a newcontext_idkwarg on_prepare_message_for_a2a. - Add a
context_idfallback chain (session →message.additional_properties["context_id"]→ random UUID) and filtercontext_idout of wire metadata. - Extend streaming/run plumbing to pass
SessionContext/AgentSessionthrough_map_a2a_streamand optionally emit intermediate task content.
| from agent_framework import ( | ||
| AgentResponse, | ||
| AgentResponseUpdate, | ||
| AgentSession, | ||
| BaseAgent, | ||
| Content, | ||
| ContinuationToken, | ||
| HistoryProvider, | ||
| Message, | ||
| ResponseStream, | ||
| SessionContext, | ||
| normalize_messages, | ||
| prepend_agent_framework_to_user_agent, | ||
| ) |
There was a problem hiding this comment.
HistoryProvider is imported from agent_framework, but the core package exports BaseHistoryProvider (and InMemoryHistoryProvider) rather than a HistoryProvider symbol. This will raise an ImportError at runtime. Use BaseHistoryProvider here and update the isinstance(provider, ...) check accordingly.
| provider_session = session | ||
| if provider_session is None and self.context_providers: | ||
| provider_session = AgentSession() | ||
|
|
||
| session_context = SessionContext( | ||
| session_id=provider_session.session_id if provider_session else None, | ||
| service_session_id=provider_session.service_session_id if provider_session else None, | ||
| input_messages=normalized_messages or [], | ||
| options={}, | ||
| ) | ||
|
|
||
| response = ResponseStream( | ||
| self._map_a2a_stream(a2a_stream, background=background), | ||
| self._map_a2a_stream( | ||
| a2a_stream, | ||
| background=background, | ||
| emit_intermediate=stream, | ||
| session=provider_session, | ||
| session_context=session_context, | ||
| ), |
There was a problem hiding this comment.
The PR description focuses on context_id propagation, but this change also introduces new behavior around context provider lifecycle (SessionContext, auto-creating AgentSession, invoking before_run/after_run) and streaming intermediate task updates. If this is intentional, it should be called out in the PR description (and ideally split if it’s unrelated), since it changes observable behavior beyond context_id handling.
| normalized_messages = normalize_messages(messages) | ||
|
|
||
| # Derive context_id from session when available so the remote agent | ||
| # can correlate messages belonging to the same conversation. | ||
| context_id: str | None = session.session_id if session else None | ||
|
|
||
| if continuation_token is not None: | ||
| a2a_stream: AsyncIterable[A2AStreamItem] = self.client.resubscribe( | ||
| TaskIdParams(id=continuation_token["task_id"]) | ||
| ) | ||
| else: | ||
| normalized_messages = normalize_messages(messages) | ||
| a2a_message = self._prepare_message_for_a2a(normalized_messages[-1]) | ||
| if not normalized_messages: | ||
| raise ValueError("At least one message is required when starting a new task (no continuation_token).") | ||
| a2a_message = self._prepare_message_for_a2a(normalized_messages[-1], context_id=context_id) | ||
| a2a_stream = self.client.send_message(a2a_message) |
There was a problem hiding this comment.
This adds context_id derivation from session.session_id and new fallback behavior in _prepare_message_for_a2a, but there are existing unit tests for A2AAgent and none currently validate that run(..., session=...) actually sends an A2AMessage with the expected context_id (or that additional_properties['context_id'] is honored/filtered from metadata). Add focused tests to prevent regressions in session/context correlation.
|
Addressing Copilot's review comments: 1. 2. SessionContext/provider lifecycle scope — Same situation. The
3. Missing tests — Valid feedback. I'll push a dedicated test file for context_id propagation. |
Summary
Fixes #4663 — Supersedes #4713 (rebased onto current
main).A2AAgent.run()accepts asessionparameter but never usessession.session_idwhen constructingthe outbound A2A message. This means the remote agent cannot correlate multiple messages belonging
to the same conversation.
Changes
context_idfrom session —session.session_idis used as the A2Acontext_idwhen a session is provided.
_prepare_message_for_a2asignature — accepts an optionalcontext_idkeyword argument.context_idpriority: session →message.additional_properties["context_id"]→ random UUID.sessionparameter's new behavior and thecontext_idparameter.Contribution Checklist