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
15 changes: 15 additions & 0 deletions a2a_agents/python/adk/samples/orchestrator/agent_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import asyncio
import logging
import json
from typing import List, Optional, override
from google.adk.agents.invocation_context import new_invocation_context_id
from google.adk.events.event_actions import EventActions
Expand Down Expand Up @@ -84,6 +85,20 @@ def convert_event_to_a2a_events_and_save_surface_id_to_subagent_name(
)

for a2a_event in a2a_events:
# Try to populate subagent agent card if available.
subagent_card = None
if (active_subagent_name := event.author):
# We need to find the subagent by name
if (subagent := next((sub for sub in invocation_context.agent.sub_agents if sub.name == active_subagent_name), None)):
try:
subagent_card = json.loads(subagent.description)
except Exception:
logger.warning(f"Failed to parse agent description for {active_subagent_name}")
if subagent_card:
if a2a_event.metadata is None:
a2a_event.metadata = {}
a2a_event.metadata["a2a_subagent"] = subagent_card

for a2a_part in a2a_event.status.message.parts:
if (
is_a2ui_part(a2a_part)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
>
@if (isRoleAgent(role)) {
<agent-header
[agentName]="role.name"
[agentName]="getAgentName(role)"
[agentIconUrl]="role.iconUrl"
[agentThoughts]="agentThoughts()"
[showProgressIndicator]="showProgressIndicator()"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export class Message {
/** Service for managing chat interactions. */
private readonly chatService = inject(ChatService);

protected getAgentName(role: UiAgent) {
const rootagentName = role.name;
return role.subagentName ? `${rootagentName} + ${role.subagentName}` : rootagentName;
}

/** Agent thought contents. */
protected readonly agentThoughts = computed(() =>
this.message().contents.filter((content) => containsAgentThought(content)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import { AgentCard, SendMessageSuccessResponse } from '@a2a-js/sdk';
import { PART_RESOLVERS } from '@a2a_chat_canvas/a2a-renderer/tokens';
import { A2A_SERVICE } from '@a2a_chat_canvas/interfaces/a2a-service';
import { UiMessage, UiMessageContent } from '@a2a_chat_canvas/types/ui-message';
import { UiAgent, UiMessage, UiMessageContent } from '@a2a_chat_canvas/types/ui-message';
import { extractA2aPartsFromResponse } from '@a2a_chat_canvas/utils/a2a';
import { extractA2uiDataParts } from '@a2a_chat_canvas/utils/a2ui';
import { convertPartToUiMessageContent } from '@a2a_chat_canvas/utils/ui-message-utils';
Expand Down Expand Up @@ -148,6 +148,7 @@ export class ChatService {

this.updateLastMessage((msg) => ({
...msg,
role: this.createRole(response),
contents: [...msg.contents, ...newContents],
status: 'completed',
lastUpdated: new Date().toISOString(),
Expand Down Expand Up @@ -246,15 +247,36 @@ export class ChatService {
type: 'ui_message',
id: uuid(),
contextId: this.contextId() ?? '',
role: {
type: 'ui_agent',
name: this.agentCard()?.name ?? 'Agent',
iconUrl: this.agentCard()?.iconUrl ?? 'gemini-color.svg',
},
role: this.createRole(),
contents: [],
status: 'pending',
created: nowTimestamp,
lastUpdated: nowTimestamp,
};
}

/**
* Creates the agent role based on the agent card and the message response if available.
*
* @param response The reponse message received from the agent.
* @returns A new UiAgent object representing the agent that the user is chatting with.
*/
private createRole(response?: SendMessageSuccessResponse): UiAgent {
const rootagentRole: UiAgent = {
type: 'ui_agent',
name: this.agentCard()?.name ?? 'Agent',
iconUrl: this.agentCard()?.iconUrl ?? 'gemini-color.svg',
};

const subagentCard = response?.result?.metadata?.['a2a_subagent'];
if (!subagentCard) {
return rootagentRole;
}
const agentRole: UiAgent = {
...rootagentRole,
subagentName: (subagentCard as AgentCard).name,
}

return agentRole;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ export interface UiAgent {
readonly name: string;
/** The URL of the agent's icon. */
readonly iconUrl: string;
/** The display name of the sub-agent. */
readonly subagentName?: string;
/** The URL of the sub-agent's icon. */
readonly subagentIconUrl?: string;
}

/**
Expand Down