Skip to content

Commit 0410edf

Browse files
committed
WIP
Signed-off-by: Emmanuel Hugonnet <[email protected]>
1 parent 325b58e commit 0410edf

File tree

7 files changed

+70
-48
lines changed

7 files changed

+70
-48
lines changed

client/transport/jsonrpc/src/main/java/io/a2a/client/transport/jsonrpc/JSONRPCTransport.java

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,8 @@ public void sendMessageStreaming(MessageSendParams request, Consumer<StreamingEv
110110
@Nullable Consumer<Throwable> errorConsumer, @Nullable ClientCallContext context) throws A2AClientException {
111111
checkNotNullParam("request", request);
112112
checkNotNullParam("eventConsumer", eventConsumer);
113-
SendStreamingMessageRequest sendStreamingMessageRequest = new SendStreamingMessageRequest.Builder()
114-
.jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
115-
.method(SendStreamingMessageRequest.METHOD)
116-
.params(request)
117-
.build(); // id will be randomly generated
118-
119113
PayloadAndHeaders payloadAndHeaders = applyInterceptors(SendStreamingMessageRequest.METHOD,
120-
sendStreamingMessageRequest, agentCard, context);
114+
ProtoUtils.ToProto.sendMessageRequest(request), agentCard, context);
121115

122116
final AtomicReference<CompletableFuture<Void>> ref = new AtomicReference<>();
123117
SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer);
@@ -279,14 +273,8 @@ public void resubscribe(TaskIdParams request, Consumer<StreamingEventKind> event
279273
checkNotNullParam("request", request);
280274
checkNotNullParam("eventConsumer", eventConsumer);
281275
checkNotNullParam("errorConsumer", errorConsumer);
282-
SubscribeToTaskRequest taskResubscriptionRequest = new SubscribeToTaskRequest.Builder()
283-
.jsonrpc(JSONRPCMessage.JSONRPC_VERSION)
284-
.method(SubscribeToTaskRequest.METHOD)
285-
.params(request)
286-
.build(); // id will be randomly generated
287-
288276
PayloadAndHeaders payloadAndHeaders = applyInterceptors(SubscribeToTaskRequest.METHOD,
289-
taskResubscriptionRequest, agentCard, context);
277+
ProtoUtils.ToProto.subscribeToTaskRequest(request), agentCard, context);
290278

291279
AtomicReference<CompletableFuture<Void>> ref = new AtomicReference<>();
292280
SSEEventListener sseEventListener = new SSEEventListener(eventConsumer, errorConsumer);

http-client/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
<groupId>${project.groupId}</groupId>
2222
<artifactId>a2a-java-sdk-spec</artifactId>
2323
</dependency>
24+
<dependency>
25+
<groupId>${project.groupId}</groupId>
26+
<artifactId>a2a-java-sdk-spec-grpc</artifactId>
27+
</dependency>
28+
<dependency>
29+
<groupId>com.google.protobuf</groupId>
30+
<artifactId>protobuf-java-util</artifactId>
31+
</dependency>
2432

2533
<dependency>
2634
<groupId>org.junit.jupiter</groupId>

http-client/src/main/java/io/a2a/client/http/A2ACardResolver.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
package io.a2a.client.http;
22

3-
import static io.a2a.util.Utils.unmarshalFrom;
43

54
import java.io.IOException;
65
import java.net.URI;
76
import java.net.URISyntaxException;
87
import java.util.Map;
98

10-
import com.fasterxml.jackson.core.JsonProcessingException;
11-
import com.fasterxml.jackson.core.type.TypeReference;
9+
import io.a2a.grpc.utils.JSONRPCUtils;
10+
import io.a2a.grpc.utils.ProtoUtils;
1211
import io.a2a.spec.A2AClientError;
1312
import io.a2a.spec.A2AClientJSONError;
1413
import io.a2a.spec.AgentCard;
14+
import io.a2a.spec.JSONRPCError;
1515
import org.jspecify.annotations.Nullable;
1616

1717
public class A2ACardResolver {
@@ -21,8 +21,6 @@ public class A2ACardResolver {
2121

2222
private static final String DEFAULT_AGENT_CARD_PATH = "/.well-known/agent-card.json";
2323

24-
private static final TypeReference<AgentCard> AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {};
25-
2624
/**
2725
* Get the agent card for an A2A agent.
2826
* The {@code JdkA2AHttpClient} will be used to fetch the agent card.
@@ -106,8 +104,10 @@ public AgentCard getAgentCard() throws A2AClientError, A2AClientJSONError {
106104
}
107105

108106
try {
109-
return unmarshalFrom(body, AGENT_CARD_TYPE_REFERENCE);
110-
} catch (JsonProcessingException e) {
107+
io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder();
108+
JSONRPCUtils.parseJsonString(body, agentCardBuilder);
109+
return ProtoUtils.FromProto.agentCard(agentCardBuilder);
110+
} catch (JSONRPCError e) {
111111
throw new A2AClientJSONError("Could not unmarshal agent card response", e);
112112
}
113113

http-client/src/test/java/io/a2a/client/http/A2ACardResolverTest.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.a2a.client.http;
22

3-
import static io.a2a.util.Utils.OBJECT_MAPPER;
4-
import static io.a2a.util.Utils.unmarshalFrom;
53
import static org.junit.jupiter.api.Assertions.assertEquals;
64
import static org.junit.jupiter.api.Assertions.assertFalse;
75
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -10,7 +8,10 @@
108
import java.util.concurrent.CompletableFuture;
119
import java.util.function.Consumer;
1210

13-
import com.fasterxml.jackson.core.type.TypeReference;
11+
import com.google.protobuf.InvalidProtocolBufferException;
12+
import com.google.protobuf.util.JsonFormat;
13+
import io.a2a.grpc.utils.JSONRPCUtils;
14+
import io.a2a.grpc.utils.ProtoUtils;
1415
import io.a2a.spec.A2AClientError;
1516
import io.a2a.spec.A2AClientJSONError;
1617
import io.a2a.spec.AgentCard;
@@ -20,7 +21,6 @@
2021
public class A2ACardResolverTest {
2122

2223
private static final String AGENT_CARD_PATH = "/.well-known/agent-card.json";
23-
private static final TypeReference<AgentCard> AGENT_CARD_TYPE_REFERENCE = new TypeReference<>() {};
2424

2525
@Test
2626
public void testConstructorStripsSlashes() throws Exception {
@@ -72,13 +72,23 @@ public void testGetAgentCardSuccess() throws Exception {
7272
A2ACardResolver resolver = new A2ACardResolver(client, "http://example.com/");
7373
AgentCard card = resolver.getAgentCard();
7474

75-
AgentCard expectedCard = unmarshalFrom(JsonMessages.AGENT_CARD, AGENT_CARD_TYPE_REFERENCE);
76-
String expected = OBJECT_MAPPER.writeValueAsString(expectedCard);
75+
AgentCard expectedCard = unmarshalFrom(JsonMessages.AGENT_CARD);
76+
String expected = printAgentCard(expectedCard);
7777

78-
String requestCardString = OBJECT_MAPPER.writeValueAsString(card);
78+
String requestCardString = printAgentCard(card);
7979
assertEquals(expected, requestCardString);
8080
}
8181

82+
private AgentCard unmarshalFrom(String body) {
83+
io.a2a.grpc.AgentCard.Builder agentCardBuilder = io.a2a.grpc.AgentCard.newBuilder();
84+
JSONRPCUtils.parseJsonString(body, agentCardBuilder);
85+
return ProtoUtils.FromProto.agentCard(agentCardBuilder);
86+
}
87+
88+
private String printAgentCard(AgentCard agentCard) throws InvalidProtocolBufferException {
89+
return JsonFormat.printer().print(ProtoUtils.ToProto.agentCard(agentCard));
90+
}
91+
8292
@Test
8393
public void testGetAgentCardJsonDecodeError() throws Exception {
8494
TestHttpClient client = new TestHttpClient();

spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
import com.google.gson.stream.JsonWriter;
1313
import com.google.protobuf.InvalidProtocolBufferException;
1414
import com.google.protobuf.util.JsonFormat;
15+
import io.a2a.grpc.StreamResponse;
1516
import io.a2a.spec.CancelTaskRequest;
1617
import io.a2a.spec.CancelTaskResponse;
1718
import io.a2a.spec.DeleteTaskPushNotificationConfigRequest;
1819
import io.a2a.spec.DeleteTaskPushNotificationConfigResponse;
1920
import io.a2a.spec.GetAuthenticatedExtendedCardRequest;
21+
import io.a2a.spec.GetAuthenticatedExtendedCardResponse;
2022
import io.a2a.spec.GetTaskPushNotificationConfigRequest;
2123
import io.a2a.spec.GetTaskPushNotificationConfigResponse;
2224
import io.a2a.spec.GetTaskRequest;
@@ -120,6 +122,20 @@ public static JSONRPCRequest<?> parseRequestBody(String body) throws JsonProcess
120122
}
121123
}
122124

125+
public static StreamResponse parseResponseEvent(String body) throws JsonProcessingException {
126+
JsonElement jelement = JsonParser.parseString(body);
127+
JsonObject jsonRpc = jelement.getAsJsonObject();
128+
String version = getAndValidateJsonrpc(jsonRpc);
129+
Object id = getAndValidateId(jsonRpc);
130+
JsonElement paramsNode = jsonRpc.get("result");
131+
if(jsonRpc.has("error")) {
132+
throw processError(jsonRpc.getAsJsonObject("error"));
133+
}
134+
StreamResponse.Builder builder = StreamResponse.newBuilder();
135+
parseRequestBody(paramsNode, builder);
136+
return builder.build();
137+
}
138+
123139
public static JSONRPCResponse<?> parseResponseBody(String body, String method) throws JsonProcessingException {
124140
JsonElement jelement = JsonParser.parseString(body);
125141
JsonObject jsonRpc = jelement.getAsJsonObject();
@@ -172,18 +188,17 @@ public static JSONRPCResponse<?> parseResponseBody(String body, String method) t
172188
return new DeleteTaskPushNotificationConfigResponse(id);
173189
}
174190
case GetAuthenticatedExtendedCardRequest.METHOD -> {
175-
return new DeleteTaskPushNotificationConfigResponse(id);
191+
io.a2a.grpc.AgentCard.Builder builder = io.a2a.grpc.AgentCard.newBuilder();
192+
parseRequestBody(paramsNode, builder);
193+
return new GetAuthenticatedExtendedCardResponse(id, ProtoUtils.FromProto.agentCard(builder));
176194
}
177195
default ->
178196
throw new MethodNotFoundJsonMappingException("Invalid method", getIdIfPossible(jsonRpc));
179197
}
180198
}
181199

182200
public static JSONRPCResponse<?> parseError(JsonObject error, Object id, String method) throws JsonProcessingException {
183-
String message = error.has("message") ? error.get("message").getAsString() : null;
184-
Integer code = error.has("code") ? error.get("code").getAsInt() : null;
185-
String data = error.has("data") ? error.get("data").toString(): null;
186-
JSONRPCError rpcError = new JSONRPCError(code, message, data);
201+
JSONRPCError rpcError = processError(error);
187202
switch (method) {
188203
case GetTaskRequest.METHOD -> {
189204
return new GetTaskResponse(id, rpcError);
@@ -214,18 +229,25 @@ public static JSONRPCResponse<?> parseError(JsonObject error, Object id, String
214229
}
215230
}
216231

232+
private static JSONRPCError processError(JsonObject error) {
233+
String message = error.has("message") ? error.get("message").getAsString() : null;
234+
Integer code = error.has("code") ? error.get("code").getAsInt() : null;
235+
String data = error.has("data") ? error.get("data").toString() : null;
236+
return new JSONRPCError(code, message, data);
237+
}
238+
217239
protected static void parseRequestBody(JsonElement jsonRpc, com.google.protobuf.Message.Builder builder) throws JSONRPCError {
218240
try (Writer writer = new StringWriter()) {
219241
GSON.toJson(jsonRpc, writer);
220-
JSONRPCUtils.parseRequestBody(writer.toString(), builder);
242+
parseJsonString(writer.toString(), builder);
221243
} catch (IOException e) {
222244
log.log(Level.SEVERE, "Error parsing JSON request body: {0}", jsonRpc);
223245
log.log(Level.SEVERE, "Parse error details", e);
224246
throw new InvalidParamsError("Failed to parse request body: " + e.getMessage());
225247
}
226248
}
227249

228-
protected static void parseRequestBody(String body, com.google.protobuf.Message.Builder builder) throws JSONRPCError {
250+
public static void parseJsonString(String body, com.google.protobuf.Message.Builder builder) throws JSONRPCError {
229251
try {
230252
JsonFormat.parser().merge(body, builder);
231253
} catch (InvalidProtocolBufferException e) {

spec-grpc/src/main/java/io/a2a/grpc/utils/ProtoUtils.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import io.a2a.grpc.mapper.MessageSendParamsMapper;
1818
import io.a2a.grpc.mapper.SetTaskPushNotificationConfigMapper;
1919
import io.a2a.grpc.mapper.StreamResponseMapper;
20-
import io.a2a.grpc.mapper.SubscribeToTaskRequestMapper;
2120
import io.a2a.grpc.mapper.TaskArtifactUpdateEventMapper;
2221
import io.a2a.grpc.mapper.TaskIdParamsMapper;
2322
import io.a2a.grpc.mapper.TaskMapper;
@@ -166,11 +165,6 @@ public static io.a2a.grpc.StreamResponse taskOrMessageStream(StreamingEventKind
166165
throw new IllegalArgumentException("Unsupported event type: " + eventKind);
167166
}
168167
}
169-
170-
public static io.a2a.grpc.SubscribeToTaskRequest subscribeToTaskRequest(io.a2a.spec.SubscribeToTaskRequest request) {
171-
return SubscribeToTaskRequestMapper.INSTANCE.toProto(request);
172-
}
173-
174168
}
175169

176170
public static class FromProto {

spec-grpc/src/test/java/io/a2a/grpc/utils/ToProtoTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public void convertAgentCard() {
6161
.tags(Collections.singletonList("hello world"))
6262
.examples(List.of("hi", "hello world"))
6363
.build()))
64-
.protocolVersion("0.2.5")
64+
.protocolVersion("0.4.0")
6565
.build();
6666
io.a2a.grpc.AgentCard result = ProtoUtils.ToProto.agentCard(agentCard);
6767
assertEquals("Hello World Agent", result.getName());
@@ -75,7 +75,7 @@ public void convertAgentCard() {
7575
assertEquals("text", result.getDefaultInputModes(0));
7676
assertEquals(1, result.getDefaultOutputModesCount());
7777
assertEquals("text", result.getDefaultOutputModes(0));
78-
assertEquals("0.2.5", result.getProtocolVersion());
78+
assertEquals("0.4.0", result.getProtocolVersion());
7979
agentCard = new AgentCard.Builder()
8080
.name("Hello World Agent")
8181
.description("Just a hello world agent")
@@ -96,12 +96,12 @@ public void convertAgentCard() {
9696
.tags(Collections.singletonList("hello world"))
9797
.examples(List.of("hi", "hello world"))
9898
.build()))
99-
// .iconUrl("http://example.com/icon.svg")
99+
// .iconUrl("http://example.com/icon.svg")
100100
.securitySchemes(Map.of("basic", new HTTPAuthSecurityScheme.Builder().scheme("basic").description("Basic Auth").build()))
101101
.security(List.of(Map.of("oauth", List.of("read"))))
102-
.protocolVersion("0.2.5")
102+
.protocolVersion("0.4.0")
103103
.build();
104-
result = ProtoUtils.ToProto.agentCard(agentCard);
104+
result = ProtoUtils.ToProto.agentCard(agentCard);
105105
assertEquals("Hello World Agent", result.getName());
106106
assertEquals("Just a hello world agent", result.getDescription());
107107
assertEquals(1, result.getSupportedInterfacesList().size());
@@ -113,7 +113,7 @@ public void convertAgentCard() {
113113
assertEquals("text", result.getDefaultInputModes(0));
114114
assertEquals(1, result.getDefaultOutputModesCount());
115115
assertEquals("text", result.getDefaultOutputModes(0));
116-
assertEquals("0.2.5", result.getProtocolVersion());
116+
assertEquals("0.4.0", result.getProtocolVersion());
117117
assertEquals(1, result.getSecurityCount());
118118
assertEquals(1, result.getSecurity(0).getSchemesMap().size());
119119
assertEquals(true, result.getSecurity(0).getSchemesMap().containsKey("oauth"));
@@ -163,7 +163,7 @@ public void convertTask() {
163163
assertEquals(false, result.getArtifacts(0).getParts(0).hasData());
164164
assertEquals("text", result.getArtifacts(0).getParts(0).getText());
165165
assertEquals(1, result.getHistoryCount());
166-
assertEquals("context-1234", result.getHistory(0).getContextId());
166+
assertEquals("context-1234", result.getHistory(0).getContextId());
167167
assertEquals("message-1234", result.getHistory(0).getMessageId());
168168
assertEquals(ROLE_USER, result.getHistory(0).getRole());
169169
assertEquals(1, result.getHistory(0).getPartsCount());

0 commit comments

Comments
 (0)