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
5 changes: 5 additions & 0 deletions a2a/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@
<version>${truth.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
70 changes: 70 additions & 0 deletions contrib/samples/a2a_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Google ADK A2A Agent Server Sample

This sample demonstrates how to expose a Google ADK (Agent Development Kit)
agent via the A2A (Agent-to-Agent) protocol using A2A SDK and Quarkus service.

## Overview

The application implements a simple conversational agent that checks whether
given numbers are prime numbers. It uses the `LlmAgent` from the Google ADK and
exposes it via an A2A server.

### Key Components

* **`Agent.java`**: Defines the `LlmAgent` instance (`check_prime_agent`) and
the `checkPrime` tool function it uses to verify numbers.
* **`AgentCardProducer.java`**: Loads and provides the `AgentCard` metadata
(from `agent.json`) which defines the agent's identity and capabilities in
the A2A network.
* **`AgentExecutorProducer.java`**: Configures and provides the A2A
`AgentExecutor`, implemented by the ADK library to wire ADK-owned agents
automatically.
* **`StartupConfig.java`**: Contains initialization logic, such as registering
JSON modules for the Vert.x/Quarkus runtime.
* **`application.properties`**: Contains a configuration for the Quarkus
service and A2A, such as port where application will be exposed, application
name and event processing timeouts.

## Building the Project

You can build the project using Maven:

```shell
mvn clean install
```

The Java server can be started using `mvn` as follow (don't forget to set your
GOOGLE_API_KEY before running the service):

```bash
export GOOGLE_API_KEY=<YOUR_API_KEY>

cd contrib/samples/a2a_server
mvn quarkus:dev
```

## Sample request

```bash
curl -X POST http://localhost:9090 \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": "cli-check-2",
"method": "message/stream",
"params": {
"message": {
"kind": "message",
"contextId": "cli-demo-context",
"messageId": "cli-check-2",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Is 2 prime?"
}
]
}
}
}'
```
142 changes: 142 additions & 0 deletions contrib/samples/a2a_server/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-samples</artifactId>
<version>0.7.1-SNAPSHOT</version><!-- {x-version-update:google-adk:current} -->
<relativePath>..</relativePath>
</parent>

<artifactId>google-adk-sample-a2a-agent</artifactId>
<packaging>jar</packaging>

<name>Google ADK - Sample - A2A Agent Server</name>
<description>Demonstrates exposing ADK agent via A2A.</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
<google-adk.version>${project.version}</google-adk.version>
<google-adk-a2a.version>${project.version}</google-adk-a2a.version>
<a2a.sdk.version>0.3.0.Beta1</a2a.sdk.version>
<quarkus.platform.version>3.30.6</quarkus.platform.version>
<flogger.version>0.8</flogger.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
<version>${a2a.sdk.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>${google-adk.version}</version>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-a2a</artifactId>
<version>${google-adk-a2a.version}</version>
</dependency>

<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-spec</artifactId>
<version>${a2a.sdk.version}</version>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-routes</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jackson</artifactId>
</dependency>

<dependency>
<groupId>io.github.a2asdk</groupId>
<artifactId>a2a-java-sdk-client</artifactId>
<version>${a2a.sdk.version}</version>
</dependency>

<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>flogger</artifactId>
<version>${flogger.version}</version>
</dependency>

<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>google-extensions</artifactId>
<version>${flogger.version}</version>
</dependency>

<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>flogger-system-backend</artifactId>
<version>${flogger.version}</version>
</dependency>

</dependencies>

<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.json</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.google.adk.samples.a2aagent;

import io.a2a.server.PublicAgentCard;
import io.a2a.spec.AgentCard;
import io.a2a.util.Utils;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/** Produces the {@link AgentCard} from the bundled JSON resources. */
@ApplicationScoped
public class AgentCardProducer {

@Produces
@PublicAgentCard
public AgentCard agentCard() {
try (InputStream is = getClass().getResourceAsStream("/agent/agent.json")) {
if (is == null) {
throw new RuntimeException("agent.json not found in resources");
}

// Read the JSON file content
String json = new String(is.readAllBytes(), StandardCharsets.UTF_8);

// Use the SDK's built-in mapper to convert JSON string to AgentCard record
return Utils.OBJECT_MAPPER.readValue(json, AgentCard.class);

} catch (Exception e) {
throw new RuntimeException("Failed to load AgentCard from JSON", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.google.adk.samples.a2aagent;

import com.google.adk.a2a.executor.AgentExecutorConfig;
import com.google.adk.samples.a2aagent.agent.Agent;
import com.google.adk.sessions.InMemorySessionService;
import io.a2a.server.agentexecution.AgentExecutor;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import org.eclipse.microprofile.config.inject.ConfigProperty;

/** Produces the {@link AgentExecutor} instance that handles agent interactions. */
@ApplicationScoped
public class AgentExecutorProducer {

@ConfigProperty(name = "my.adk.app.name", defaultValue = "default-app")
String appName;

@Produces
public AgentExecutor agentExecutor() {
InMemorySessionService sessionService = new InMemorySessionService();
return new com.google.adk.a2a.executor.AgentExecutor.Builder()
.agent(Agent.ROOT_AGENT)
.appName(appName)
.sessionService(sessionService)
.agentExecutorConfig(AgentExecutorConfig.builder().build())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.google.adk.samples.a2aagent;

import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import io.quarkus.runtime.StartupEvent;
import io.vertx.core.json.jackson.DatabindCodec;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

/** Configuration applied on startup, such as Jackson module registrations. */
@ApplicationScoped
public class StartupConfig {

void onStart(@Observes StartupEvent ev) {
// Register globally for Vert.x's internal JSON handling
DatabindCodec.mapper().registerModule(new JavaTimeModule());
}
}
Loading