Skip to content

Commit

Permalink
configure tools using environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
alexcheng1982 committed Apr 15, 2024
1 parent 2f79905 commit 8efe803
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 10 deletions.
7 changes: 7 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>

<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>2.1.6</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.alexcheng1982.agentappbuilder.core.tool

import java.lang.reflect.Method
import java.util.function.Supplier

interface AgentToolFactory<out T : AgentTool<*, *>> {
Expand All @@ -17,4 +18,66 @@ abstract class BaseConfigurableAgentToolFactory<out T : ConfigurableAgentTool<*,
override fun create(): T {
return create(configProvider.get())
}
}

abstract class EnvironmentVariableConfigurableAgentToolFactory<out T : ConfigurableAgentTool<*, *, CONFIG>, CONFIG>(
configClass: Class<CONFIG>,
environmentVariablePrefix: String
) :
BaseConfigurableAgentToolFactory<T, CONFIG>(
EnvironmentVariableConfigProvider(
configClass,
environmentVariablePrefix
)
)

open class EnvironmentVariableConfigProvider<C>(
private val configClass: Class<C>,
private val environmentVariablePrefix: String
) :
Supplier<C> {
override fun get(): C {
val instance = configClass.getDeclaredConstructor().newInstance()
val setters = getPropertySetters()
getEnvironmentVariables().forEach { (key, value) ->
setters[key]?.run {
invokeMethod(instance, this, value)
}
}
return instance
}

private fun invokeMethod(instance: C, method: Method, value: String) {
if (method.parameterCount != 1) {
return
}
val parameter = method.parameters[0]
val parameterValue = when (parameter.type) {
Long::class.java -> value.toLongOrNull()
Int::class.java -> value.toIntOrNull()
Double::class.java -> value.toDoubleOrNull()
Float::class.java -> value.toFloatOrNull()
else -> value
}
method.invoke(instance, parameterValue)
}

private fun getPropertySetters(): Map<String, Method> {
return configClass.methods.filter {
it.name.startsWith("set")
}.associateBy {
it.name.removePrefix("set").lowercase()
}
}

private fun getEnvironmentVariables(): Map<String, String> {
val prefix = environmentVariablePrefix.lowercase()
return System.getenv()
.filterKeys {
it.lowercase().startsWith(prefix)
}.mapKeys { entry ->
entry.key.lowercase()
.removePrefix(prefix)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.github.alexcheng1982.agentappbuilder.core.tool

class ToolExecutionException(
private val tool: AgentTool<*, *>,
cause: Throwable?
) : RuntimeException(cause) {
override val message: String
get() = "Execution error in tool ${tool.name()}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.github.alexcheng1982.agentappbuilder.core.tool

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import uk.org.webcompere.systemstubs.SystemStubs.withEnvironmentVariable
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension
import kotlin.properties.Delegates
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

@ExtendWith(SystemStubsExtension::class)
class EnvironmentVariableConfigProviderTest {

class TestConfig {
lateinit var value: String
var intValue by Delegates.notNull<Int>()
var longValue by Delegates.notNull<Long>()
var doubleValue by Delegates.notNull<Double>()
var floatValue by Delegates.notNull<Float>()
}

class TestConfigProvider :
EnvironmentVariableConfigProvider<TestConfig>(
TestConfig::class.java,
"testConfig_"
)

@Test
fun testConfigProvider() {
withEnvironmentVariable("testConfig_value", "hello")
.and("testConfig_intValue", "100")
.and("testConfig_longValue", "1000")
.and("testConfig_doubleValue", "1.01")
.and("testConfig_floatValue", "2.02")
.execute {
val config = TestConfigProvider().get()
assertNotNull(config)
assertEquals("hello", config.value)
assertEquals(100, config.intValue)
assertEquals(1000, config.longValue)
assertEquals(1.01, config.doubleValue)
assertEquals(2.02f, config.floatValue)
}
}
}
7 changes: 7 additions & 0 deletions tools/write-local-file/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>

<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>2.1.6</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
package io.github.alexcheng1982.agentappbuilder.tool.writelocalfile;

public record WriteLocalFileConfig(String basePath) {
public class WriteLocalFileConfig {

private String basePath;

public String getBasePath() {
return basePath;
}

public void setBasePath(String basePath) {
this.basePath = basePath;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.alexcheng1982.agentappbuilder.tool.writelocalfile;

public record WriteLocalFileRequest(String filename, String url,
public record WriteLocalFileRequest(String filename,
String url,
String content) {

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.alexcheng1982.agentappbuilder.tool.writelocalfile;

import io.github.alexcheng1982.agentappbuilder.core.tool.ConfigurableAgentTool;
import io.github.alexcheng1982.agentappbuilder.core.tool.ToolExecutionException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -42,6 +43,7 @@ public String description() {
@Override
public WriteLocalFileResponse apply(
WriteLocalFileRequest request) {
logger.info("Request: {}", request);
try {
var savePath = calculateSavePath(request);
if (StringUtils.isNotEmpty(request.content())) {
Expand All @@ -53,18 +55,19 @@ public WriteLocalFileResponse apply(
savePath.toFile());
}
String path = savePath.toAbsolutePath().toString();
logger.info("Written to file {}", path);
return new WriteLocalFileResponse(path);
var response = new WriteLocalFileResponse(path);
logger.info("Response: {}", response);
return response;
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
throw new ToolExecutionException(this, e);
}
}

private Path calculateSavePath(WriteLocalFileRequest request)
throws IOException {
Path basePath =
StringUtils.isEmpty(config.basePath()) ? Files.createTempDirectory(
"write-local-file-") : Paths.get(config.basePath());
StringUtils.isEmpty(config.getBasePath()) ? Files.createTempDirectory(
"write-local-file-") : Paths.get(config.getBasePath());
var filename = Optional.ofNullable(
StringUtils.trimToNull(request.filename()))
.orElseGet(() -> UUID.randomUUID().toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package io.github.alexcheng1982.agentappbuilder.tool.writelocalfile;

import io.github.alexcheng1982.agentappbuilder.core.tool.BaseConfigurableAgentToolFactory;
import io.github.alexcheng1982.agentappbuilder.core.tool.EnvironmentVariableConfigurableAgentToolFactory;
import org.jetbrains.annotations.NotNull;

public class WriteLocalFileToolFactory extends
BaseConfigurableAgentToolFactory<WriteLocalFileTool, WriteLocalFileConfig> {
EnvironmentVariableConfigurableAgentToolFactory<WriteLocalFileTool, WriteLocalFileConfig> {

public WriteLocalFileToolFactory() {
super(() -> new WriteLocalFileConfig(null));
super(WriteLocalFileConfig.class, "writeLocalFile_");
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static uk.org.webcompere.systemstubs.SystemStubs.withEnvironmentVariable;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;

@ExtendWith(SystemStubsExtension.class)
class WriteLocalFileToolTest {

@Test
Expand All @@ -28,4 +33,20 @@ void testDownload() {
var response = tool.apply(request);
assertNotNull(response.path());
}

@Test
void testWithConfig() throws Exception {
var tempPath = Files.createTempDirectory("some_path_");
withEnvironmentVariable("writeLocalFile_basePath",
tempPath.toAbsolutePath().toString())
.execute(() -> {
var tool = new WriteLocalFileToolFactory().create();
String content = "world";
var request = new WriteLocalFileRequest("hello.txt", null, content);
var response = tool.apply(request);
assertNotNull(response.path());
assertTrue(
response.path().startsWith(tempPath.toAbsolutePath().toString()));
});
}
}

0 comments on commit 8efe803

Please sign in to comment.