Skip to content
Closed
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
33 changes: 29 additions & 4 deletions core/src/a2a/a2a_remote_agent_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ export interface UserFunctionCall {
}

/**
* Returns a UserFunctionCall when the event at index has a FunctionResponse.
* Returns a UserFunctionCall when the event at `index` contains a
* FunctionResponse that can be traced back to a preceding FunctionCall event.
*
* @param session - The session whose event history to inspect.
* @param index - Index of the candidate event to examine.
* @returns The matching `UserFunctionCall`, or `undefined` if the event at
* `index` is not a user function-response event or has no preceding call.
*/
export function getUserFunctionCallAt(
session: Session,
Expand Down Expand Up @@ -62,6 +68,10 @@ export function getUserFunctionCallAt(

/**
* Checks if an event contains a function call with the given ID.
*
* @param event - The event to inspect.
* @param callId - The function call ID to look for.
* @returns `true` if a part in the event has a matching `functionCall.id`.
*/
export function isFunctionCallEvent(event: AdkEvent, callId: string): boolean {
if (!event || !event.content || !event.content.parts) {
Expand All @@ -75,6 +85,10 @@ export function isFunctionCallEvent(event: AdkEvent, callId: string): boolean {

/**
* Finds the first part with a FunctionResponse and returns the call ID.
*
* @param event - The event to inspect.
* @returns The `id` of the first FunctionResponse part, or `undefined` if
* none is found.
*/
export function getFunctionResponseCallId(event: AdkEvent): string | undefined {
if (!event || !event.content || !event.content.parts) {
Expand All @@ -89,8 +103,13 @@ export function getFunctionResponseCallId(event: AdkEvent): string | undefined {
}

/**
* Returns content parts for all events not present in the remote session
* and a2a contextId if found in a remote agent event metadata.
* Returns A2A content parts for all events not yet seen by the remote agent,
* along with the A2A context ID found in the most recent remote agent event.
*
* @param ctx - The current invocation context, used to identify the remote
* agent's authored events.
* @param session - The local session whose event history to diff.
* @returns An object with the missing `parts` and an optional `contextId`.
*/
export function toMissingRemoteSessionParts(
ctx: InvocationContext,
Expand Down Expand Up @@ -137,7 +156,13 @@ export function toMissingRemoteSessionParts(
}

/**
* Wraps an agent event as a user message for context.
* Wraps an agent event as a user message so it can be sent as context to a
* remote agent that only accepts user-role messages.
*
* @param ctx - The current invocation context.
* @param agentEvent - The agent-authored event to reframe as a user message.
* @returns A new event with `author: 'user'` whose parts summarise the
* original agent event's text, function calls, and function responses.
*/
export function presentAsUserMessage(
ctx: InvocationContext,
Expand Down
15 changes: 14 additions & 1 deletion core/src/a2a/event_converter_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ import {toA2AParts, toGenAIPart, toGenAIParts} from './part_converter_utils.js';

/**
* Converts a session Event to an A2A Message.
*
* @param event - The ADK event to convert.
* @param appName - The name of the ADK application.
* @param userId - The ID of the current user.
* @param sessionId - The ID of the current session.
* @returns An A2A message with the event's parts and metadata.
*/
export function toA2AMessage(
event: AdkEvent,
Expand All @@ -63,7 +69,14 @@ export function toA2AMessage(
}

/**
* Converts an A2A Event to a Session Event.
* Converts an A2A Event to an ADK Session Event.
*
* @param event - The A2A event to convert (message, task, artifact update, or
* status update).
* @param invocationId - The ADK invocation ID to attach to the resulting event.
* @param agentName - The name of the agent to use as the event author.
* @returns The converted ADK event, or `undefined` if the A2A event type
* produces no content.
*/
export function toAdkEvent(
event: A2AEvent,
Expand Down
18 changes: 18 additions & 0 deletions core/src/a2a/metadata_converter_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export enum AdkMetadataKeys {

/**
* Creates ADK Event metadata from an A2A Event.
*
* @param a2aEvent - The A2A event to extract metadata from.
* @returns A record containing the ADK task ID and context ID keys.
*/
export function getAdkEventMetadata(
a2aEvent: A2AEvent,
Expand All @@ -58,6 +61,12 @@ export function getAdkEventMetadata(

/**
* Creates A2A Event metadata from an ADK Event.
*
* @param adkEvent - The ADK event to extract metadata from.
* @param appName - The name of the ADK application.
* @param userId - The ID of the current user.
* @param sessionId - The ID of the current session.
* @returns A record of A2A metadata keys populated from the ADK event.
*/
export function getA2AEventMetadata(
adkEvent: AdkEvent,
Expand Down Expand Up @@ -91,6 +100,11 @@ export function getA2AEventMetadata(

/**
* Creates A2A Session metadata from ADK Event invocation metadata.
*
* @param appName - The name of the ADK application.
* @param userId - The ID of the current user.
* @param sessionId - The ID of the current session.
* @returns A record of A2A metadata keys for app name, user ID, and session ID.
*/
export function getA2ASessionMetadata({
appName,
Expand All @@ -110,6 +124,10 @@ export function getA2ASessionMetadata({

/**
* Creates A2A Event metadata from ADK Event actions.
*
* @param actions - The ADK event actions to extract escalation and transfer
* metadata from.
* @returns A record with escalate and transfer-to-agent A2A metadata keys.
*/
export function getA2AEventMetadataFromActions(
actions: AdkEventActions,
Expand Down
57 changes: 51 additions & 6 deletions core/src/a2a/part_converter_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ enum DataPartType {
}

/**
* Converts GenAI Parts to A2A Parts.
* Converts an array of GenAI Parts to A2A Parts.
*
* @param parts - The GenAI parts to convert. Defaults to an empty array.
* @param longRunningToolIDs - IDs of function calls that are long-running.
* @returns An array of A2A parts.
*/
export function toA2AParts(
parts: GenAIPart[] = [],
Expand All @@ -45,7 +49,11 @@ export function toA2AParts(
}

/**
* Converts a GenAI Part to an A2A Part.
* Converts a single GenAI Part to the appropriate A2A Part type.
*
* @param part - The GenAI part to convert.
* @param longRunningToolIDs - IDs of function calls that are long-running.
* @returns The corresponding A2A part (text, file, or data).
*/
export function toA2APart(
part: GenAIPart,
Expand All @@ -64,6 +72,9 @@ export function toA2APart(

/**
* Converts a GenAI Text Part to an A2A Text Part.
*
* @param part - The GenAI part containing a text field.
* @returns An A2A text part, with thought metadata attached if applicable.
*/
export function toA2ATextPart(part: GenAIPart): A2APart {
const a2aPart: A2APart = {kind: 'text', text: part.text || ''};
Expand All @@ -79,6 +90,10 @@ export function toA2ATextPart(part: GenAIPart): A2APart {

/**
* Converts a GenAI File Part to an A2A File Part.
*
* @param part - The GenAI part containing `fileData` or `inlineData`.
* @returns An A2A file part with URI or bytes depending on the source.
* @throws {Error} If the part contains neither `fileData` nor `inlineData`.
*/
export function toA2AFilePart(part: GenAIPart): A2APart {
const metadata: Record<string, unknown> = {};
Expand Down Expand Up @@ -112,7 +127,11 @@ export function toA2AFilePart(part: GenAIPart): A2APart {
}

/**
* Converts a GenAI Data Part to an A2A Data Part.
* Converts a GenAI Data Part (function call/response or code) to an A2A Data Part.
*
* @param part - The GenAI part containing structured data.
* @param longRunningToolIDs - IDs of function calls that are long-running.
* @returns An A2A data part with the appropriate type metadata.
*/
export function toA2ADataPart(
part: GenAIPart,
Expand Down Expand Up @@ -172,6 +191,12 @@ export function toA2ADataPart(
};
}

/**
* Converts an A2A Message to a GenAI Content object.
*
* @param a2aMessage - The A2A message to convert.
* @returns A GenAI user or model content object based on the message role.
*/
export function toGenAIContent(a2aMessage: Message): GenAIContent {
const parts = toGenAIParts(a2aMessage.parts);

Expand All @@ -181,14 +206,21 @@ export function toGenAIContent(a2aMessage: Message): GenAIContent {
}

/**
* Converts an A2A Part to a GenAI Part.
* Converts an array of A2A Parts to GenAI Parts.
*
* @param a2aParts - The A2A parts to convert.
* @returns An array of GenAI parts.
*/
export function toGenAIParts(a2aParts: A2APart[]): GenAIPart[] {
return a2aParts.map((a2aPart) => toGenAIPart(a2aPart));
}

/**
* Converts an A2A Part to a GenAI Part.
* Converts a single A2A Part to the appropriate GenAI Part type.
*
* @param a2aPart - The A2A part to convert.
* @returns The corresponding GenAI part.
* @throws {Error} If the A2A part has an unrecognized `kind`.
*/
export function toGenAIPart(a2aPart: A2APart): GenAIPart {
if (a2aPart.kind === 'text') {
Expand All @@ -208,6 +240,9 @@ export function toGenAIPart(a2aPart: A2APart): GenAIPart {

/**
* Converts an A2A Text Part to a GenAI Part.
*
* @param a2aPart - The A2A text part to convert.
* @returns A GenAI part with `text` and optional `thought` flag.
*/
export function toGenAIPartText(a2aPart: A2ATextPart): GenAIPart {
return {
Expand All @@ -218,6 +253,11 @@ export function toGenAIPartText(a2aPart: A2ATextPart): GenAIPart {

/**
* Converts an A2A File Part to a GenAI Part.
*
* @param a2aPart - The A2A file part containing bytes or a URI.
* @returns A GenAI part with `inlineData` or `fileData` depending on the
* source, with optional video metadata attached.
* @throws {Error} If the file part contains neither bytes nor a URI.
*/
export function toGenAIPartFile(a2aPart: A2AFilePart): GenAIPart {
const part: GenAIPart = {};
Expand Down Expand Up @@ -247,7 +287,12 @@ export function toGenAIPartFile(a2aPart: A2AFilePart): GenAIPart {
}

/**
* Converts an A2A Data Part to a GenAI Part.
* Converts an A2A Data Part to the appropriate GenAI Part.
*
* @param a2aPart - The A2A data part containing structured data and type metadata.
* @returns A GenAI part with the appropriate function call, function response,
* or code execution fields, falling back to a JSON text part if the type is
* unrecognized.
*/
export function toGenAIPartData(a2aPart: A2ADataPart): GenAIPart {
if (!a2aPart.data) {
Expand Down