|
16 | 16 |
|
17 | 17 |
|
18 | 18 | /** |
19 | | - * Constants and utility methods related to the A2A protocol. |
| 19 | + * Utility class providing convenience methods for working with the A2A Protocol. |
| 20 | + * <p> |
| 21 | + * This class offers static helper methods for common A2A operations: |
| 22 | + * <ul> |
| 23 | + * <li><b>Message creation:</b> Simplified construction of user and agent messages</li> |
| 24 | + * <li><b>Agent card retrieval:</b> Fetching agent metadata from URLs</li> |
| 25 | + * </ul> |
| 26 | + * <p> |
| 27 | + * These utilities simplify client code by providing concise alternatives to the builder |
| 28 | + * APIs for routine operations. |
| 29 | + * <p> |
| 30 | + * <b>Example usage:</b> |
| 31 | + * <pre>{@code |
| 32 | + * // Get agent card |
| 33 | + * AgentCard card = A2A.getAgentCard("http://localhost:9999"); |
| 34 | + * |
| 35 | + * // Create and send a user message |
| 36 | + * Message userMsg = A2A.toUserMessage("What's the weather today?"); |
| 37 | + * client.sendMessage(userMsg); |
| 38 | + * |
| 39 | + * // Create a message with context and task IDs |
| 40 | + * Message contextMsg = A2A.createUserTextMessage( |
| 41 | + * "Continue the conversation", |
| 42 | + * "session-123", // contextId |
| 43 | + * "task-456" // taskId |
| 44 | + * ); |
| 45 | + * client.sendMessage(contextMsg); |
| 46 | + * }</pre> |
| 47 | + * |
| 48 | + * @see Message |
| 49 | + * @see AgentCard |
| 50 | + * @see io.a2a.client.Client |
20 | 51 | */ |
21 | 52 | public class A2A { |
22 | 53 |
|
23 | 54 | /** |
24 | | - * Convert the given text to a user message. |
| 55 | + * Create a simple user message from text. |
| 56 | + * <p> |
| 57 | + * This is the most common way to create messages when sending requests to agents. |
| 58 | + * The message will have: |
| 59 | + * <ul> |
| 60 | + * <li>role: USER</li> |
| 61 | + * <li>parts: Single {@link io.a2a.spec.TextPart} with the provided text</li> |
| 62 | + * <li>Auto-generated message ID</li> |
| 63 | + * </ul> |
| 64 | + * <p> |
| 65 | + * Example: |
| 66 | + * <pre>{@code |
| 67 | + * Message msg = A2A.toUserMessage("Tell me a joke"); |
| 68 | + * client.sendMessage(msg); |
| 69 | + * }</pre> |
25 | 70 | * |
26 | | - * @param text the message text |
27 | | - * @return the user message |
| 71 | + * @param text the message text (required) |
| 72 | + * @return a user message with the specified text |
| 73 | + * @see #toUserMessage(String, String) |
| 74 | + * @see #createUserTextMessage(String, String, String) |
28 | 75 | */ |
29 | 76 | public static Message toUserMessage(String text) { |
30 | 77 | return toMessage(text, Message.Role.USER, null); |
31 | 78 | } |
32 | 79 |
|
33 | 80 | /** |
34 | | - * Convert the given text to a user message. |
| 81 | + * Create a user message from text with a specific message ID. |
| 82 | + * <p> |
| 83 | + * Use this when you need to control the message ID for tracking or correlation purposes. |
| 84 | + * <p> |
| 85 | + * Example: |
| 86 | + * <pre>{@code |
| 87 | + * String messageId = UUID.randomUUID().toString(); |
| 88 | + * Message msg = A2A.toUserMessage("Process this request", messageId); |
| 89 | + * // Store messageId for later correlation |
| 90 | + * client.sendMessage(msg); |
| 91 | + * }</pre> |
35 | 92 | * |
36 | | - * @param text the message text |
| 93 | + * @param text the message text (required) |
37 | 94 | * @param messageId the message ID to use |
38 | | - * @return the user message |
| 95 | + * @return a user message with the specified text and ID |
| 96 | + * @see #toUserMessage(String) |
39 | 97 | */ |
40 | 98 | public static Message toUserMessage(String text, String messageId) { |
41 | 99 | return toMessage(text, Message.Role.USER, messageId); |
42 | 100 | } |
43 | 101 |
|
44 | 102 | /** |
45 | | - * Convert the given text to an agent message. |
| 103 | + * Create a simple agent message from text. |
| 104 | + * <p> |
| 105 | + * This is typically used in testing or when constructing agent responses programmatically. |
| 106 | + * Most client applications receive agent messages via {@link io.a2a.client.MessageEvent} |
| 107 | + * rather than creating them manually. |
| 108 | + * <p> |
| 109 | + * Example: |
| 110 | + * <pre>{@code |
| 111 | + * // Testing scenario |
| 112 | + * Message agentResponse = A2A.toAgentMessage("Here's the answer: 42"); |
| 113 | + * }</pre> |
46 | 114 | * |
47 | | - * @param text the message text |
48 | | - * @return the agent message |
| 115 | + * @param text the message text (required) |
| 116 | + * @return an agent message with the specified text |
| 117 | + * @see #toAgentMessage(String, String) |
49 | 118 | */ |
50 | 119 | public static Message toAgentMessage(String text) { |
51 | 120 | return toMessage(text, Message.Role.AGENT, null); |
52 | 121 | } |
53 | 122 |
|
54 | 123 | /** |
55 | | - * Convert the given text to an agent message. |
| 124 | + * Create an agent message from text with a specific message ID. |
| 125 | + * <p> |
| 126 | + * Example: |
| 127 | + * <pre>{@code |
| 128 | + * Message agentResponse = A2A.toAgentMessage("Processing complete", "msg-789"); |
| 129 | + * }</pre> |
56 | 130 | * |
57 | | - * @param text the message text |
| 131 | + * @param text the message text (required) |
58 | 132 | * @param messageId the message ID to use |
59 | | - * @return the agent message |
| 133 | + * @return an agent message with the specified text and ID |
60 | 134 | */ |
61 | 135 | public static Message toAgentMessage(String text, String messageId) { |
62 | 136 | return toMessage(text, Message.Role.AGENT, messageId); |
63 | 137 | } |
64 | 138 |
|
65 | 139 | /** |
66 | 140 | * Create a user message with text content and optional context and task IDs. |
| 141 | + * <p> |
| 142 | + * This method is useful when continuing a conversation or working with a specific task: |
| 143 | + * <ul> |
| 144 | + * <li><b>contextId:</b> Links message to a conversation session</li> |
| 145 | + * <li><b>taskId:</b> Associates message with an existing task</li> |
| 146 | + * </ul> |
| 147 | + * <p> |
| 148 | + * Example - continuing a conversation: |
| 149 | + * <pre>{@code |
| 150 | + * // First message creates context |
| 151 | + * Message msg1 = A2A.toUserMessage("What's your name?"); |
| 152 | + * client.sendMessage(msg1); |
| 153 | + * String contextId = ...; // Get from response |
| 154 | + * |
| 155 | + * // Follow-up message uses contextId |
| 156 | + * Message msg2 = A2A.createUserTextMessage( |
| 157 | + * "What else can you do?", |
| 158 | + * contextId, |
| 159 | + * null // no specific task |
| 160 | + * ); |
| 161 | + * client.sendMessage(msg2); |
| 162 | + * }</pre> |
| 163 | + * <p> |
| 164 | + * Example - adding to an existing task: |
| 165 | + * <pre>{@code |
| 166 | + * Message msg = A2A.createUserTextMessage( |
| 167 | + * "Add this information too", |
| 168 | + * "session-123", |
| 169 | + * "task-456" // Continue working on this task |
| 170 | + * ); |
| 171 | + * client.sendMessage(msg); |
| 172 | + * }</pre> |
67 | 173 | * |
68 | 174 | * @param text the message text (required) |
69 | 175 | * @param contextId the context ID to use (optional) |
70 | 176 | * @param taskId the task ID to use (optional) |
71 | | - * @return the user message |
| 177 | + * @return a user message with the specified text, context, and task IDs |
| 178 | + * @see #createAgentTextMessage(String, String, String) |
| 179 | + * @see Message#contextId() |
| 180 | + * @see Message#taskId() |
72 | 181 | */ |
73 | 182 | public static Message createUserTextMessage(String text, String contextId, String taskId) { |
74 | 183 | return toMessage(text, Message.Role.USER, null, contextId, taskId); |
75 | 184 | } |
76 | 185 |
|
77 | 186 | /** |
78 | 187 | * Create an agent message with text content and optional context and task IDs. |
| 188 | + * <p> |
| 189 | + * This is typically used in testing or when constructing agent responses programmatically. |
79 | 190 | * |
80 | 191 | * @param text the message text (required) |
81 | 192 | * @param contextId the context ID to use (optional) |
82 | 193 | * @param taskId the task ID to use (optional) |
83 | | - * @return the agent message |
| 194 | + * @return an agent message with the specified text, context, and task IDs |
| 195 | + * @see #createUserTextMessage(String, String, String) |
84 | 196 | */ |
85 | 197 | public static Message createAgentTextMessage(String text, String contextId, String taskId) { |
86 | 198 | return toMessage(text, Message.Role.AGENT, null, contextId, taskId); |
87 | 199 | } |
88 | 200 |
|
89 | 201 | /** |
90 | 202 | * Create an agent message with custom parts and optional context and task IDs. |
| 203 | + * <p> |
| 204 | + * This method allows creating messages with multiple parts (text, images, files, etc.) |
| 205 | + * instead of just simple text. Useful for complex agent responses or testing. |
| 206 | + * <p> |
| 207 | + * Example - message with text and image: |
| 208 | + * <pre>{@code |
| 209 | + * List<Part<?>> parts = List.of( |
| 210 | + * new TextPart("Here's a chart of the data:"), |
| 211 | + * new ImagePart("https://example.com/chart.png", "Chart showing sales data") |
| 212 | + * ); |
| 213 | + * Message msg = A2A.createAgentPartsMessage(parts, "session-123", "task-456"); |
| 214 | + * }</pre> |
91 | 215 | * |
92 | | - * @param parts the message parts (required) |
| 216 | + * @param parts the message parts (required, must not be empty) |
93 | 217 | * @param contextId the context ID to use (optional) |
94 | 218 | * @param taskId the task ID to use (optional) |
95 | | - * @return the agent message |
| 219 | + * @return an agent message with the specified parts, context, and task IDs |
| 220 | + * @throws IllegalArgumentException if parts is null or empty |
| 221 | + * @see io.a2a.spec.Part |
| 222 | + * @see io.a2a.spec.TextPart |
96 | 223 | */ |
97 | 224 | public static Message createAgentPartsMessage(List<Part<?>> parts, String contextId, String taskId) { |
98 | 225 | if (parts == null || parts.isEmpty()) { |
@@ -130,56 +257,140 @@ private static Message toMessage(List<Part<?>> parts, Message.Role role, String |
130 | 257 | } |
131 | 258 |
|
132 | 259 | /** |
133 | | - * Get the agent card for an A2A agent. |
| 260 | + * Retrieve the agent card for an A2A agent. |
| 261 | + * <p> |
| 262 | + * This is the standard way to discover an agent's capabilities before creating a client. |
| 263 | + * The agent card is fetched from the well-known endpoint: {@code <agentUrl>/.well-known/agent-card.json} |
| 264 | + * <p> |
| 265 | + * Example: |
| 266 | + * <pre>{@code |
| 267 | + * // Get agent card |
| 268 | + * AgentCard card = A2A.getAgentCard("http://localhost:9999"); |
| 269 | + * |
| 270 | + * // Check capabilities |
| 271 | + * System.out.println("Agent: " + card.name()); |
| 272 | + * System.out.println("Supports streaming: " + card.capabilities().streaming()); |
| 273 | + * |
| 274 | + * // Create client |
| 275 | + * Client client = Client.builder(card) |
| 276 | + * .withTransport(...) |
| 277 | + * .build(); |
| 278 | + * }</pre> |
134 | 279 | * |
135 | 280 | * @param agentUrl the base URL for the agent whose agent card we want to retrieve |
136 | 281 | * @return the agent card |
137 | | - * @throws A2AClientError If an HTTP error occurs fetching the card |
138 | | - * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 282 | + * @throws io.a2a.spec.A2AClientError if an HTTP error occurs fetching the card |
| 283 | + * @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 284 | + * @see #getAgentCard(A2AHttpClient, String) |
| 285 | + * @see #getAgentCard(String, String, java.util.Map) |
| 286 | + * @see AgentCard |
139 | 287 | */ |
140 | 288 | public static AgentCard getAgentCard(String agentUrl) throws A2AClientError, A2AClientJSONError { |
141 | 289 | return getAgentCard(new JdkA2AHttpClient(), agentUrl); |
142 | 290 | } |
143 | 291 |
|
144 | 292 | /** |
145 | | - * Get the agent card for an A2A agent. |
| 293 | + * Retrieve the agent card using a custom HTTP client. |
| 294 | + * <p> |
| 295 | + * Use this variant when you need to customize HTTP behavior (timeouts, SSL configuration, |
| 296 | + * connection pooling, etc.). |
| 297 | + * <p> |
| 298 | + * Example: |
| 299 | + * <pre>{@code |
| 300 | + * A2AHttpClient customClient = new CustomHttpClient() |
| 301 | + * .withTimeout(Duration.ofSeconds(10)) |
| 302 | + * .withSSLContext(mySSLContext); |
| 303 | + * |
| 304 | + * AgentCard card = A2A.getAgentCard(customClient, "https://secure-agent.com"); |
| 305 | + * }</pre> |
146 | 306 | * |
147 | 307 | * @param httpClient the http client to use |
148 | 308 | * @param agentUrl the base URL for the agent whose agent card we want to retrieve |
149 | 309 | * @return the agent card |
150 | | - * @throws A2AClientError If an HTTP error occurs fetching the card |
151 | | - * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 310 | + * @throws io.a2a.spec.A2AClientError if an HTTP error occurs fetching the card |
| 311 | + * @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 312 | + * @see io.a2a.client.http.A2AHttpClient |
152 | 313 | */ |
153 | 314 | public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl) throws A2AClientError, A2AClientJSONError { |
154 | 315 | return getAgentCard(httpClient, agentUrl, null, null); |
155 | 316 | } |
156 | 317 |
|
157 | 318 | /** |
158 | | - * Get the agent card for an A2A agent. |
| 319 | + * Retrieve the agent card with custom path and authentication. |
| 320 | + * <p> |
| 321 | + * Use this variant when: |
| 322 | + * <ul> |
| 323 | + * <li>The agent card is at a non-standard location</li> |
| 324 | + * <li>Authentication is required to access the agent card</li> |
| 325 | + * </ul> |
| 326 | + * <p> |
| 327 | + * Example with authentication: |
| 328 | + * <pre>{@code |
| 329 | + * Map<String, String> authHeaders = Map.of( |
| 330 | + * "Authorization", "Bearer my-api-token", |
| 331 | + * "X-API-Key", "my-api-key" |
| 332 | + * ); |
| 333 | + * |
| 334 | + * AgentCard card = A2A.getAgentCard( |
| 335 | + * "https://secure-agent.com", |
| 336 | + * null, // Use default path |
| 337 | + * authHeaders |
| 338 | + * ); |
| 339 | + * }</pre> |
| 340 | + * <p> |
| 341 | + * Example with custom path: |
| 342 | + * <pre>{@code |
| 343 | + * AgentCard card = A2A.getAgentCard( |
| 344 | + * "https://agent.com", |
| 345 | + * "api/v2/agent-info", // Custom path |
| 346 | + * null // No auth needed |
| 347 | + * ); |
| 348 | + * // Fetches from: https://agent.com/api/v2/agent-info |
| 349 | + * }</pre> |
159 | 350 | * |
160 | 351 | * @param agentUrl the base URL for the agent whose agent card we want to retrieve |
161 | 352 | * @param relativeCardPath optional path to the agent card endpoint relative to the base |
162 | 353 | * agent URL, defaults to ".well-known/agent-card.json" |
163 | 354 | * @param authHeaders the HTTP authentication headers to use |
164 | 355 | * @return the agent card |
165 | | - * @throws A2AClientError If an HTTP error occurs fetching the card |
166 | | - * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 356 | + * @throws io.a2a.spec.A2AClientError if an HTTP error occurs fetching the card |
| 357 | + * @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema |
167 | 358 | */ |
168 | 359 | public static AgentCard getAgentCard(String agentUrl, String relativeCardPath, Map<String, String> authHeaders) throws A2AClientError, A2AClientJSONError { |
169 | 360 | return getAgentCard(new JdkA2AHttpClient(), agentUrl, relativeCardPath, authHeaders); |
170 | 361 | } |
171 | 362 |
|
172 | 363 | /** |
173 | | - * Get the agent card for an A2A agent. |
| 364 | + * Retrieve the agent card with full customization options. |
| 365 | + * <p> |
| 366 | + * This is the most flexible variant, allowing customization of: |
| 367 | + * <ul> |
| 368 | + * <li>HTTP client implementation</li> |
| 369 | + * <li>Agent card endpoint path</li> |
| 370 | + * <li>Authentication headers</li> |
| 371 | + * </ul> |
| 372 | + * <p> |
| 373 | + * Example: |
| 374 | + * <pre>{@code |
| 375 | + * A2AHttpClient customClient = new CustomHttpClient(); |
| 376 | + * Map<String, String> authHeaders = Map.of("Authorization", "Bearer token"); |
| 377 | + * |
| 378 | + * AgentCard card = A2A.getAgentCard( |
| 379 | + * customClient, |
| 380 | + * "https://agent.com", |
| 381 | + * "custom/agent-card", |
| 382 | + * authHeaders |
| 383 | + * ); |
| 384 | + * }</pre> |
174 | 385 | * |
175 | 386 | * @param httpClient the http client to use |
176 | 387 | * @param agentUrl the base URL for the agent whose agent card we want to retrieve |
177 | 388 | * @param relativeCardPath optional path to the agent card endpoint relative to the base |
178 | 389 | * agent URL, defaults to ".well-known/agent-card.json" |
179 | 390 | * @param authHeaders the HTTP authentication headers to use |
180 | 391 | * @return the agent card |
181 | | - * @throws A2AClientError If an HTTP error occurs fetching the card |
182 | | - * @throws A2AClientJSONError If the response body cannot be decoded as JSON or validated against the AgentCard schema |
| 392 | + * @throws io.a2a.spec.A2AClientError if an HTTP error occurs fetching the card |
| 393 | + * @throws io.a2a.spec.A2AClientJSONError if the response body cannot be decoded as JSON or validated against the AgentCard schema |
183 | 394 | */ |
184 | 395 | public static AgentCard getAgentCard(A2AHttpClient httpClient, String agentUrl, String relativeCardPath, Map<String, String> authHeaders) throws A2AClientError, A2AClientJSONError { |
185 | 396 | A2ACardResolver resolver = new A2ACardResolver(httpClient, agentUrl, "", relativeCardPath, authHeaders); |
|
0 commit comments