Skip to content
Open
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
28 changes: 14 additions & 14 deletions samples/java/agents/content_editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,37 @@ This sample agent can be used to proof-read and polish content. This agent is wr

2. Create a .env file in the `content_editor` directory as follows:

```bash
cp .env.example .env
```
```bash
cp .env.example .env
```

Then update the `.env` file to specify your Google AI Studio API Key (note that no quotes are needed below):
Then update the `.env` file to specify your Google AI Studio API Key (note that no quotes are needed below):

```bash
QUARKUS_LANGCHAIN4J_AI_GEMINI_API_KEY=your_api_key_here
```
```bash
QUARKUS_LANGCHAIN4J_AI_GEMINI_API_KEY=your_api_key_here
```

3. Run the Content Editor Agent

**NOTE:**
By default, the agent will start on port 10003. To override this, add the `-Dquarkus.http.port=YOUR_PORT` option at the end of the command below.
**NOTE:**
By default, the agent will start on port 10003. To override this, add the `-Dquarkus.http.port=YOUR_PORT` option at the end of the command below.

```bash
mvn quarkus:dev
```
```bash
mvn quarkus:dev
```

4. In a separate terminal, run the A2A client and use it to send a message to the agent:

```bash
# Connect to the agent (specify the agent URL with correct port)
cd samples/python/hosts/cli
cd samples/python/hosts/cli/cli_v10
uv run . --agent http://localhost:10003

# If you changed the port when starting the agent, use that port instead
# uv run . --agent http://localhost:YOUR_PORT
```

5. To make use of this agent in a content creation multi-agent system, check out the [content_creation](../../../python/hosts/content_creation/README.md) sample.
5. To make use of this agent in a content creation multi-agent system, check out the [content_creation_v10](../../../python/hosts/content_creation_v10/README.md) sample.

## Disclaimer
Important: The sample code provided is for demonstration purposes and illustrates the
Expand Down
24 changes: 23 additions & 1 deletion samples/java/agents/content_editor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<dependencies>
<dependency>
<groupId>io.github.a2asdk</groupId>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-reference-jsonrpc</artifactId>
<version>${io.a2a.sdk.version}</version>
</dependency>
Expand All @@ -32,6 +32,28 @@
<artifactId>quarkus-langchain4j-ai-gemini</artifactId>
<version>${quarkus.langchain4j.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-client</artifactId>
<version>${io.a2a.sdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.a2aproject.sdk</groupId>
<artifactId>a2a-java-sdk-client-transport-jsonrpc</artifactId>
<version>${io.a2a.sdk.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.samples.a2a;

import io.a2a.server.PublicAgentCard;
import io.a2a.spec.AgentCapabilities;
import io.a2a.spec.AgentCard;
import io.a2a.spec.AgentSkill;
import org.a2aproject.sdk.server.PublicAgentCard;
import org.a2aproject.sdk.spec.AgentCapabilities;
import org.a2aproject.sdk.spec.AgentCard;
import org.a2aproject.sdk.spec.AgentInterface;
import org.a2aproject.sdk.spec.AgentSkill;
import org.a2aproject.sdk.spec.TransportProtocol;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
Expand Down Expand Up @@ -40,23 +42,22 @@ public int getHttpPort() {
@Produces
@PublicAgentCard
public AgentCard agentCard() {
return new AgentCard.Builder()
return AgentCard.builder()
.name("Content Editor Agent")
.description("An agent that can proof-read and polish content.")
.url("http://localhost:" + getHttpPort())
.version("1.0.0")
.documentationUrl("http://example.com/docs")
.capabilities(
new AgentCapabilities.Builder()
AgentCapabilities.builder()
.streaming(true)
.pushNotifications(false)
.stateTransitionHistory(false)
.build())
.defaultInputModes(Collections.singletonList("text"))
.defaultOutputModes(Collections.singletonList("text"))
.skills(
Collections.singletonList(
new AgentSkill.Builder()
AgentSkill.builder()
.id("editor")
.name("Edits content")
.description("Edits content by proof-reading and polishing")
Expand All @@ -66,7 +67,11 @@ public AgentCard agentCard() {
"Edit the following article, make sure it has "
+ "a professional tone"))
.build()))
.protocolVersion("0.3.0")
.supportedInterfaces(
Collections.singletonList(
new AgentInterface(
TransportProtocol.JSONRPC.asString(),
"http://localhost:" + getHttpPort())))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,16 @@

import java.util.List;

import io.a2a.server.agentexecution.AgentExecutor;
import io.a2a.server.agentexecution.RequestContext;
import io.a2a.server.events.EventQueue;
import io.a2a.server.tasks.TaskUpdater;
import io.a2a.spec.JSONRPCError;
import io.a2a.spec.Message;
import io.a2a.spec.Part;
import io.a2a.spec.Task;
import io.a2a.spec.TaskNotCancelableError;
import io.a2a.spec.TaskState;
import io.a2a.spec.TextPart;
import org.a2aproject.sdk.server.agentexecution.AgentExecutor;
import org.a2aproject.sdk.server.agentexecution.RequestContext;
import org.a2aproject.sdk.server.tasks.AgentEmitter;
import org.a2aproject.sdk.spec.A2AError;
import org.a2aproject.sdk.spec.Message;
import org.a2aproject.sdk.spec.Part;
import org.a2aproject.sdk.spec.Task;
import org.a2aproject.sdk.spec.TaskNotCancelableError;
import org.a2aproject.sdk.spec.TaskState;
import org.a2aproject.sdk.spec.TextPart;

/**
* Producer for content editor agent executor.
Expand Down Expand Up @@ -72,14 +71,12 @@ private static class ContentEditorAgentExecutor implements AgentExecutor {

@Override
public void execute(final RequestContext context,
final EventQueue eventQueue) throws JSONRPCError {
final TaskUpdater updater = new TaskUpdater(context, eventQueue);

final AgentEmitter emitter) throws A2AError {
// mark the task as submitted and start working on it
if (context.getTask() == null) {
updater.submit();
emitter.submit();
}
updater.startWork();
emitter.startWork();

// extract the text from the message
final String assignment = extractTextFromMessage(
Expand All @@ -93,16 +90,16 @@ public void execute(final RequestContext context,
final List<Part<?>> parts = List.of(responsePart);

// add the response as an artifact and complete the task
updater.addArtifact(parts, null, null, null);
updater.complete();
emitter.addArtifact(parts, null, null, null);
emitter.complete();
}

private String extractTextFromMessage(final Message message) {
final StringBuilder textBuilder = new StringBuilder();
if (message.getParts() != null) {
for (final Part part : message.getParts()) {
if (message.parts() != null) {
for (final Part part : message.parts()) {
if (part instanceof TextPart textPart) {
textBuilder.append(textPart.getText());
textBuilder.append(textPart.text());
}
}
}
Expand All @@ -111,22 +108,21 @@ private String extractTextFromMessage(final Message message) {

@Override
public void cancel(final RequestContext context,
final EventQueue eventQueue) throws JSONRPCError {
final AgentEmitter emitter) throws A2AError {
final Task task = context.getTask();

if (task.getStatus().state() == TaskState.CANCELED) {
if (task.status().state() == TaskState.TASK_STATE_CANCELED) {
// task already cancelled
throw new TaskNotCancelableError();
}

if (task.getStatus().state() == TaskState.COMPLETED) {
if (task.status().state() == TaskState.TASK_STATE_COMPLETED) {
// task already completed
throw new TaskNotCancelableError();
}

// cancel the task
final TaskUpdater updater = new TaskUpdater(context, eventQueue);
updater.cancel();
emitter.cancel();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.samples.a2a;

import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import org.a2aproject.sdk.A2A;
import org.a2aproject.sdk.client.Client;
import org.a2aproject.sdk.client.ClientEvent;
import org.a2aproject.sdk.client.MessageEvent;
import org.a2aproject.sdk.client.TaskUpdateEvent;
import org.a2aproject.sdk.client.transport.jsonrpc.JSONRPCTransport;
import org.a2aproject.sdk.client.transport.jsonrpc.JSONRPCTransportConfig;
import org.a2aproject.sdk.spec.AgentCard;
import org.a2aproject.sdk.spec.Artifact;
import org.a2aproject.sdk.spec.Part;
import org.a2aproject.sdk.spec.TaskStatusUpdateEvent;
import org.a2aproject.sdk.spec.TextPart;
import org.a2aproject.sdk.spec.UpdateEvent;
import jakarta.inject.Inject;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.anyString;

@QuarkusTest
class ContentEditorAgentTest {

@ConfigProperty(name = "quarkus.http.test-port", defaultValue = "8081")
int testPort;

@InjectMock
ContentEditorAgent contentEditorAgent;

private String serverUrl() {
return "http://localhost:" + testPort;
}

@Test
void testAgentCard() throws Exception {
AgentCard card = A2A.getAgentCard(serverUrl());

assertNotNull(card);
assertEquals("Content Editor Agent", card.name());
assertNotNull(card.skills());
assertFalse(card.skills().isEmpty());
assertEquals("editor", card.skills().get(0).id());
}

@Test
void testSendMessage() throws Exception {
Mockito.when(contentEditorAgent.editContent(anyString()))
.thenReturn("Polished content from mock.");

CompletableFuture<String> responseText = new CompletableFuture<>();

AgentCard card = A2A.getAgentCard(serverUrl());
Client client = Client.builder(card)
.withTransport(JSONRPCTransport.class, new JSONRPCTransportConfig())
.addConsumer((ClientEvent event, AgentCard agentCard) -> {
if (event instanceof MessageEvent messageEvent) {
responseText.complete(extractText(messageEvent.getMessage().parts()));
} else if (event instanceof TaskUpdateEvent taskUpdate) {
UpdateEvent update = taskUpdate.getUpdateEvent();
if (update instanceof TaskStatusUpdateEvent statusUpdate
&& statusUpdate.isFinal()) {
StringBuilder text = new StringBuilder();
for (Artifact artifact : taskUpdate.getTask().artifacts()) {
text.append(extractText(artifact.parts()));
}
responseText.complete(text.toString());
}
}
})
.build();

client.sendMessage(A2A.toUserMessage("Please edit this draft text."));

String result = responseText.get(10, TimeUnit.SECONDS);
assertEquals("Polished content from mock.", result);
}

private static String extractText(final List<Part<?>> parts) {
StringBuilder sb = new StringBuilder();
if (parts != null) {
for (Part<?> part : parts) {
if (part instanceof TextPart textPart) {
sb.append(textPart.text());
}
}
}
return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
quarkus.http.test-port=0
28 changes: 14 additions & 14 deletions samples/java/agents/content_writer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,37 +17,37 @@ This sample agent can be used to generate an engaging piece of content given a c

2. Create a .env file in the `content_writer` directory as follows:

```bash
cp .env.example .env
```
```bash
cp .env.example .env
```

Then update the `.env` file to specify your Google AI Studio API Key (note that no quotes are needed below):
Then update the `.env` file to specify your Google AI Studio API Key (note that no quotes are needed below):

```bash
QUARKUS_LANGCHAIN4J_AI_GEMINI_API_KEY=your_api_key_here
```
```bash
QUARKUS_LANGCHAIN4J_AI_GEMINI_API_KEY=your_api_key_here
```

3. Run the Content Writer Agent

**NOTE:**
By default, the agent will start on port 10002. To override this, add the `-Dquarkus.http.port=YOUR_PORT` option at the end of the command below.
**NOTE:**
By default, the agent will start on port 10002. To override this, add the `-Dquarkus.http.port=YOUR_PORT` option at the end of the command below.

```bash
mvn quarkus:dev
```
```bash
mvn quarkus:dev
```

4. In a separate terminal, run the A2A client and use it to send a message to the agent:

```bash
# Connect to the agent (specify the agent URL with correct port)
cd samples/python/hosts/cli
cd samples/python/hosts/cli/cli_v10
uv run . --agent http://localhost:10002

# If you changed the port when starting the agent, use that port instead
# uv run . --agent http://localhost:YOUR_PORT
```

5. To make use of this agent in a content creation multi-agent system, check out the [content_creation](../../../python/hosts/content_creation/README.md) sample.
5. To make use of this agent in a content creation multi-agent system, check out the [content_creation_v10](../../../python/hosts/content_creation_v10/README.md) sample.

## Disclaimer
Important: The sample code provided is for demonstration purposes and illustrates the
Expand Down
Loading