diff --git a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/AgentScopeA2aServer.java b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/AgentScopeA2aServer.java index 4ecf1f471..b0a9e89ea 100644 --- a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/AgentScopeA2aServer.java +++ b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/AgentScopeA2aServer.java @@ -395,6 +395,7 @@ public AgentScopeA2aServer build() { TransportProperties.builder(TransportProtocol.JSONRPC.asString()) .host(deploymentProperties.host()) .port(deploymentProperties.port()) + .path(deploymentProperties.path()) .build()); } Map allBuilders = loadTransportBuilders(); diff --git a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/transport/DeploymentProperties.java b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/transport/DeploymentProperties.java index 6251af125..ee8da195d 100644 --- a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/transport/DeploymentProperties.java +++ b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/main/java/io/agentscope/core/a2a/server/transport/DeploymentProperties.java @@ -22,12 +22,12 @@ import org.slf4j.LoggerFactory; /** - * Some deployment relative properties, such as server default export port and host. + * Some deployment relative properties, such as server default export port and host and path. * *

When developers don't specified target {@link TransportProperties}, and want to use default transport, developers * should create this class and input port at least to make sure AgentScope can generate the url for default transport. */ -public record DeploymentProperties(String host, int port) { +public record DeploymentProperties(String host, int port, String path) { private static final Logger log = LoggerFactory.getLogger(DeploymentProperties.class); @@ -37,6 +37,8 @@ public static class Builder { private Integer port; + private String path; + public Builder host(String host) { this.host = host; return this; @@ -47,6 +49,11 @@ public Builder port(Integer port) { return this; } + public Builder path(String path) { + this.path = path; + return this; + } + public DeploymentProperties build() { if (null == host) { log.info( @@ -61,7 +68,7 @@ public DeploymentProperties build() { if (null == port) { throw new IllegalArgumentException("port must be configured."); } - return new DeploymentProperties(host, port); + return new DeploymentProperties(host, port, path); } } } diff --git a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/test/java/io/agentscope/core/a2a/server/transport/DeploymentPropertiesTest.java b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/test/java/io/agentscope/core/a2a/server/transport/DeploymentPropertiesTest.java index a5e86dbce..a60ca1714 100644 --- a/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/test/java/io/agentscope/core/a2a/server/transport/DeploymentPropertiesTest.java +++ b/agentscope-extensions/agentscope-extensions-a2a/agentscope-extensions-a2a-server/src/test/java/io/agentscope/core/a2a/server/transport/DeploymentPropertiesTest.java @@ -71,13 +71,29 @@ class DeploymentPropertiesBuildingTests { void testBuildWithAllProperties() { String host = "localhost"; Integer port = 8080; + String path = "/api"; DeploymentProperties deploymentProperties = - new DeploymentProperties.Builder().host(host).port(port).build(); + new DeploymentProperties.Builder().host(host).port(port).path(path).build(); assertNotNull(deploymentProperties); assertEquals(host, deploymentProperties.host()); assertEquals(port, deploymentProperties.port()); + assertEquals(path, deploymentProperties.path()); + } + + @Test + @DisplayName("Should build DeploymentProperties with path only") + void testBuildWithPathOnly() { + Integer port = 8080; + String path = "/api/v1"; + + DeploymentProperties deploymentProperties = + new DeploymentProperties.Builder().port(port).path(path).build(); + + assertNotNull(deploymentProperties); + assertEquals(port, deploymentProperties.port()); + assertEquals(path, deploymentProperties.path()); } @Test @@ -126,9 +142,10 @@ void testThrowExceptionWhenPortNotSpecified() { void testBuilderMethodChaining() { String host = "localhost"; Integer port = 8080; + String path = "/api"; DeploymentProperties.Builder builder = new DeploymentProperties.Builder(); - DeploymentProperties.Builder result = builder.host(host).port(port); + DeploymentProperties.Builder result = builder.host(host).port(port).path(path); assertNotNull(result); assertSame(builder, result); @@ -138,6 +155,7 @@ void testBuilderMethodChaining() { assertNotNull(deploymentProperties); assertEquals(host, deploymentProperties.host()); assertEquals(port, deploymentProperties.port()); + assertEquals(path, deploymentProperties.path()); } } @@ -150,12 +168,28 @@ class RecordFunctionalityTests { void testRecordConstructor() { String host = "localhost"; Integer port = 8080; + String path = "/api"; + + DeploymentProperties deploymentProperties = new DeploymentProperties(host, port, path); + + assertNotNull(deploymentProperties); + assertEquals(host, deploymentProperties.host()); + assertEquals(port, deploymentProperties.port()); + assertEquals(path, deploymentProperties.path()); + } + + @Test + @DisplayName("Should create record with null path") + void testRecordConstructorWithNullPath() { + String host = "localhost"; + Integer port = 8080; - DeploymentProperties deploymentProperties = new DeploymentProperties(host, port); + DeploymentProperties deploymentProperties = new DeploymentProperties(host, port, null); assertNotNull(deploymentProperties); assertEquals(host, deploymentProperties.host()); assertEquals(port, deploymentProperties.port()); + assertNull(deploymentProperties.path()); } @Test @@ -163,9 +197,10 @@ void testRecordConstructor() { void testEqualsAndHashCode() { String host = "localhost"; int port = 8080; + String path = "/api"; - DeploymentProperties deploymentProperties1 = new DeploymentProperties(host, port); - DeploymentProperties deploymentProperties2 = new DeploymentProperties(host, port); + DeploymentProperties deploymentProperties1 = new DeploymentProperties(host, port, path); + DeploymentProperties deploymentProperties2 = new DeploymentProperties(host, port, path); assertEquals(deploymentProperties1, deploymentProperties2); assertEquals(deploymentProperties1.hashCode(), deploymentProperties2.hashCode()); @@ -176,13 +211,15 @@ void testEqualsAndHashCode() { void testToString() { String host = "localhost"; int port = 8080; + String path = "/api"; - DeploymentProperties deploymentProperties = new DeploymentProperties(host, port); + DeploymentProperties deploymentProperties = new DeploymentProperties(host, port, path); String toStringResult = deploymentProperties.toString(); assertNotNull(toStringResult); assertTrue(toStringResult.contains(host)); assertTrue(toStringResult.contains(Integer.toString(port))); + assertTrue(toStringResult.contains(path)); } } } diff --git a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfiguration.java b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfiguration.java index 9fccd9abc..6c2378767 100644 --- a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfiguration.java +++ b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfiguration.java @@ -95,8 +95,10 @@ public AgentScopeA2aServer agentScopeA2aServer( List agentRegistries, List transportProperties) { AgentScopeA2aServer.Builder builder = AgentScopeA2aServer.builder(agentRunner); - builder.agentCard(buildConfigurableAgentCard(agentCardProperties)); + ConfigurableAgentCard configurableAgentCard = + buildConfigurableAgentCard(agentCardProperties); DeploymentProperties deploymentProperties = buildDeploymentProperties(environment); + builder.agentCard(configurableAgentCard); builder.deploymentProperties(deploymentProperties); builder.agentExecuteProperties(buildAgentExecuteProperties(commonProperties)); transportProperties.stream() @@ -188,10 +190,15 @@ private DeploymentProperties buildDeploymentProperties(Environment environment) environment.getProperty(Constants.DEFAULT_SERVER_EXPORT_PORT, Integer.class, 8080); String defaultServerExportAddress = environment.getProperty(Constants.DEFAULT_SERVER_EXPORT_ADDRESS); + String defaultServerExportContextPath = + environment.getProperty(Constants.DEFAULT_SERVER_EXPORT_CONTEXT_PATH); result.port(defaultServerExportPort); if (null != defaultServerExportAddress) { result.host(defaultServerExportAddress); } + if (null != defaultServerExportContextPath) { + result.path(defaultServerExportContextPath); + } return result.build(); } diff --git a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/Constants.java b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/Constants.java index cac0e0f13..8029777bd 100644 --- a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/Constants.java +++ b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/Constants.java @@ -40,4 +40,6 @@ public class Constants { public static final String DEFAULT_SERVER_EXPORT_PORT = "server.port"; public static final String DEFAULT_SERVER_EXPORT_ADDRESS = "server.address"; + + public static final String DEFAULT_SERVER_EXPORT_CONTEXT_PATH = "server.servlet.context-path"; } diff --git a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/JSONRPCProperties.java b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/JSONRPCProperties.java index 053b09354..44fce07be 100644 --- a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/JSONRPCProperties.java +++ b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/main/java/io/agentscope/spring/boot/a2a/properties/JSONRPCProperties.java @@ -46,6 +46,7 @@ public TransportProperties toTransportProperties() { return TransportProperties.builder(TransportProtocol.JSONRPC.asString()) .host(deploymentProperties.host()) .port(deploymentProperties.port()) + .path(deploymentProperties.path()) .build(); } diff --git a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/test/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfigurationTest.java b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/test/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfigurationTest.java index adec114bb..06e38bfdb 100644 --- a/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/test/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfigurationTest.java +++ b/agentscope-extensions/agentscope-spring-boot-starters/agentscope-a2a-spring-boot-starter/src/test/java/io/agentscope/spring/boot/a2a/AgentscopeA2aAutoConfigurationTest.java @@ -170,6 +170,52 @@ void shouldStartSuccessWithAddressProperties() { }); } + @Test + void shouldIncludeContextPathInAgentCardUrl() { + WebApplicationContextRunner contextRunner = + new WebApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(AgentscopeA2aAutoConfiguration.class)) + .withBean( + ReActAgent.class, + () -> ReActAgent.builder().name("mockAgent").build()) + .withPropertyValues( + "server.address=192.168.1.100", + "server.port=8081", + "server.servlet.context-path=/api"); + contextRunner.run( + context -> { + assertThat(context).hasNotFailed(); + AgentScopeA2aServer server = context.getBean(AgentScopeA2aServer.class); + AgentCard agentCard = server.getAgentCard(); + Assertions.assertEquals("http://192.168.1.100:8081/api", agentCard.url()); + // Verify additional interfaces also contain the context-path + assertThat(agentCard.additionalInterfaces()).isNotEmpty(); + assertThat(agentCard.additionalInterfaces().get(0).url()) + .isEqualTo("http://192.168.1.100:8081/api"); + }); + } + + @Test + void shouldGenerateCorrectUrlWithContextPath() { + WebApplicationContextRunner contextRunner = + new WebApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(AgentscopeA2aAutoConfiguration.class)) + .withBean( + ReActAgent.class, + () -> ReActAgent.builder().name("mockAgent").build()) + .withPropertyValues("server.servlet.context-path=/myapp/v1"); + contextRunner.run( + context -> { + assertThat(context).hasNotFailed(); + AgentScopeA2aServer server = context.getBean(AgentScopeA2aServer.class); + AgentCard agentCard = server.getAgentCard(); + // URL should contain the context-path + assertThat(agentCard.url()).contains("/myapp/v1"); + }); + } + @Test void shouldCallbackServerReadyListener() { AgentScopeA2aServer mockServer = mock(AgentScopeA2aServer.class);