refactor: clean up A2A task output for users and LLMs (#16561)

This commit is contained in:
Adam Weidman
2026-01-13 20:58:55 -05:00
committed by GitHub
parent 04f65d7b4e
commit 428e602882
3 changed files with 47 additions and 44 deletions
+9 -9
View File
@@ -124,7 +124,7 @@ describe('a2aUtils', () => {
}); });
describe('extractTaskText', () => { describe('extractTaskText', () => {
it('should extract basic task info', () => { it('should extract basic task info (clean)', () => {
const task: Task = { const task: Task = {
id: 'task-1', id: 'task-1',
contextId: 'ctx-1', contextId: 'ctx-1',
@@ -141,12 +141,12 @@ describe('a2aUtils', () => {
}; };
const result = extractTaskText(task); const result = extractTaskText(task);
expect(result).toContain('ID: task-1'); expect(result).not.toContain('ID: task-1');
expect(result).toContain('State: working'); expect(result).not.toContain('State: working');
expect(result).toContain('Status Message: Processing...'); expect(result).toBe('Processing...');
}); });
it('should extract artifacts', () => { it('should extract artifacts with headers', () => {
const task: Task = { const task: Task = {
id: 'task-1', id: 'task-1',
contextId: 'ctx-1', contextId: 'ctx-1',
@@ -162,10 +162,10 @@ describe('a2aUtils', () => {
}; };
const result = extractTaskText(task); const result = extractTaskText(task);
expect(result).toContain('Artifacts:'); expect(result).toContain('Artifact (Report):');
expect(result).toContain(' - Name: Report'); expect(result).toContain('This is the report.');
expect(result).toContain(' Content:'); expect(result).not.toContain('Artifacts:');
expect(result).toContain(' This is the report.'); expect(result).not.toContain(' - Name: Report');
}); });
}); });
}); });
+29 -26
View File
@@ -18,14 +18,11 @@ import type {
* Handles Text, Data (JSON), and File parts. * Handles Text, Data (JSON), and File parts.
*/ */
export function extractMessageText(message: Message | undefined): string { export function extractMessageText(message: Message | undefined): string {
if (!message || !message.parts) { if (!message) {
return ''; return '';
} }
const parts = message.parts return extractPartsText(message.parts);
.map((part) => extractPartText(part))
.filter(Boolean);
return parts.join('\n');
} }
/** /**
@@ -56,41 +53,47 @@ export function extractPartText(part: Part): string {
} }
/** /**
* Extracts a human-readable text summary from a Task object. * Extracts a clean, human-readable text summary from a Task object.
* Includes status, ID, and any artifact content. * Includes the status message and any artifact content with context headers.
* Technical metadata like ID and State are omitted for better clarity and token efficiency.
*/ */
export function extractTaskText(task: Task): string { export function extractTaskText(task: Task): string {
let output = `ID: ${task.id}\n`; const parts: string[] = [];
output += `State: ${task.status.state}\n`;
// Status Message // Status Message
const statusMessageText = extractMessageText(task.status.message); const statusMessageText = extractMessageText(task.status?.message);
if (statusMessageText) { if (statusMessageText) {
output += `Status Message: ${statusMessageText}\n`; parts.push(statusMessageText);
} }
// Artifacts // Artifacts
if (task.artifacts && task.artifacts.length > 0) { if (task.artifacts) {
output += `Artifacts:\n`;
for (const artifact of task.artifacts) { for (const artifact of task.artifacts) {
output += ` - Name: ${artifact.name}\n`; const artifactContent = extractPartsText(artifact.parts);
if (artifact.parts && artifact.parts.length > 0) {
// Treat artifact parts as a message for extraction
const artifactContent = artifact.parts
.map((p) => extractPartText(p))
.filter(Boolean)
.join('\n');
if (artifactContent) { if (artifactContent) {
// Indent content for readability const header = artifact.name
const indentedContent = artifactContent.replace(/^/gm, ' '); ? `Artifact (${artifact.name}):`
output += ` Content:\n${indentedContent}\n`; : 'Artifact:';
} parts.push(`${header}\n${artifactContent}`);
} }
} }
} }
return output; return parts.join('\n\n');
}
/**
* Extracts text from an array of parts.
*/
function extractPartsText(parts: Part[] | undefined): string {
if (!parts || parts.length === 0) {
return '';
}
return parts
.map((p) => extractPartText(p))
.filter(Boolean)
.join('\n');
} }
// Type Guards // Type Guards
@@ -166,16 +166,16 @@ export class RemoteAgentInvocation extends BaseToolInvocation<
}); });
// Extract the output text // Extract the output text
const resultData = response; const outputText =
let outputText = ''; response.kind === 'task'
? extractTaskText(response)
: response.kind === 'message'
? extractMessageText(response)
: JSON.stringify(response);
if (resultData.kind === 'message') { debugLogger.debug(
outputText = extractMessageText(resultData); `[RemoteAgent] Response from ${this.definition.name}:\n${JSON.stringify(response, null, 2)}`,
} else if (resultData.kind === 'task') { );
outputText = extractTaskText(resultData);
} else {
outputText = JSON.stringify(resultData);
}
return { return {
llmContent: [{ text: outputText }], llmContent: [{ text: outputText }],