Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Configuration File and Management #8

Merged
merged 3 commits into from
Sep 11, 2023
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
4 changes: 2 additions & 2 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 33 additions & 13 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<maven.compiler.source>20</maven.compiler.source>
<maven.compiler.target>20</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jacksonVersion>2.15.2</jacksonVersion>
<junit5Version>5.10.0</junit5Version>
<log4j2Version>2.20.0</log4j2Version>
<systemStubsVersion>2.1.1</systemStubsVersion>
</properties>
<dependencies>
<dependency>
Expand All @@ -37,19 +41,41 @@
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
<scope>test</scope>
<scope>${junit5Version}</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.0</version>
<version>${junit5Version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-core</artifactId>
<version>${systemStubsVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>${systemStubsVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
<version>${jacksonVersion}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-toml</artifactId>
<version>${jacksonVersion}</version>
</dependency>
<dependency>
<groupId>io.javalin</groupId>
Expand All @@ -61,26 +87,20 @@
<artifactId>httpclient5-fluent</artifactId>
<version>5.2.1</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.20.0</version>
<version>${log4j2Version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
<version>${log4j2Version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.20.0</version>
<version>${log4j2Version}</version>
</dependency>
<dependency>
<groupId>com.knuddels</groupId>
Expand Down
23 changes: 0 additions & 23 deletions src/main/java/com/meta/chatbridge/Configuration.java

This file was deleted.

40 changes: 37 additions & 3 deletions src/main/java/com/meta/chatbridge/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,44 @@

package com.meta.chatbridge;

import io.javalin.Javalin;
import com.google.common.base.Preconditions;
import com.meta.chatbridge.configuration.ConfigurationUtils;
import com.meta.chatbridge.configuration.RootConfiguration;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import org.checkerframework.checker.nullness.qual.Nullable;

public class Main {
private static final Javalin APP = Javalin.create();

public static void main(String[] strings) {}
static Path configurationFile(String[] strings) {
int configKeyLocation = Arrays.binarySearch(strings, "--config");
@Nullable String propertyConfigPath = System.getProperty("cp4m_configuration_file");
@Nullable String envConfigPath = System.getenv("CP4M_CONFIGURATION_FILE");
Path configPath;
if (configKeyLocation >= 0) {
int configPathLocation = configKeyLocation + 1;
Preconditions.checkArgument(
configPathLocation < strings.length,
"A path to the location of the file must be given if --config is provided");
configPath = Path.of(strings[configPathLocation]);
} else if (propertyConfigPath != null) {
configPath = Path.of(propertyConfigPath);
} else if (envConfigPath != null) {
configPath = Path.of(envConfigPath);
} else {
throw new IllegalArgumentException(
"No configuration file found. A configuration file must be provided via the commandline argument '--config', via the system property cp4m_configuration_file, or via the environment variable CP4M_CONFIGURATION_FILE");
}
Preconditions.checkArgument(
Files.exists(configPath), "given configuration at " + configPath + " does not exist");
return configPath;
}

public static void main(String[] strings) throws IOException {
Path configurationFile = configurationFile(strings);
RootConfiguration configuration = ConfigurationUtils.loadConfigurationFile(configurationFile);
configuration.toServicesRunner().start();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Pipeline<T extends Message> {
public class Service<T extends Message> {

private static final Logger LOGGER = LoggerFactory.getLogger(Pipeline.class);
private static final Logger LOGGER = LoggerFactory.getLogger(Service.class);
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final MessageHandler<T> handler;
private final ChatStore<T> store;
private final LLMPlugin<T> llmPlugin;

private final String path;

public Pipeline(
public Service(
ChatStore<T> store, MessageHandler<T> handler, LLMPlugin<T> llmPlugin, String path) {
this.handler = Objects.requireNonNull(handler);
this.store = Objects.requireNonNull(store);
Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/meta/chatbridge/ServiceConfiguration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.meta.chatbridge;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import java.util.Objects;

public class ServiceConfiguration {
private final String webhookPath;
private final String handler;
private final String store;
private final String plugin;

@JsonCreator
ServiceConfiguration(
@JsonProperty("webhook_path") String webhookPath,
@JsonProperty("handler") String handler,
@JsonProperty("store") String store,
@JsonProperty("plugin") String plugin) {
Preconditions.checkArgument(
webhookPath != null && webhookPath.startsWith("/"),
"webhook_path must be present and it must start with a forward slash (/)");
this.webhookPath = webhookPath;
this.handler = Objects.requireNonNull(handler, "handler must be present");
this.store = Objects.requireNonNull(store, "store must be present");
this.plugin = Objects.requireNonNull(plugin, "plugin must be present");
}

public String webhookPath() {
return webhookPath;
}

public String handler() {
return handler;
}

public String store() {
return store;
}

public String plugin() {
return plugin;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,37 @@
import java.util.Set;
import org.checkerframework.common.returnsreceiver.qual.This;

public class PipelinesRunner implements AutoCloseable {
public class ServicesRunner implements AutoCloseable {
private final Javalin app = Javalin.create();
private boolean started = false;
private int port = 8080;

private final Set<Pipeline<?>> pipelines = new HashSet<>();
private final Set<Service<?>> services = new HashSet<>();

private PipelinesRunner() {}
private ServicesRunner() {}

public static PipelinesRunner newInstance() {
return new PipelinesRunner();
public static ServicesRunner newInstance() {
return new ServicesRunner();
}

public @This PipelinesRunner start() {
public @This ServicesRunner start() {
if (!started) {
started = true;
app.start(port);
}
return this;
}

public @This PipelinesRunner pipeline(Pipeline<?> pipeline) {
Preconditions.checkState(!started, "cannot add pipeline, server already started");
if (pipelines.add(pipeline)) {
pipeline.register(app);
public @This ServicesRunner service(Service<?> service) {
Preconditions.checkState(!started, "cannot add service, server already started");
if (services.add(service)) {
service.register(app);
}
return this;
}

public Collection<Pipeline<?>> pipelines() {
return Collections.unmodifiableCollection(pipelines);
public Collection<Service<?>> services() {
return Collections.unmodifiableCollection(services);
}

public int port() {
Expand All @@ -62,7 +62,7 @@ public int port() {
* @param port the port the server will start on
* @return this
*/
public @This PipelinesRunner port(int port) {
public @This ServicesRunner port(int port) {
Preconditions.checkState(!started, "cannot change port, server already started");
this.port = port;
return this;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

package com.meta.chatbridge.configuration;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.dataformat.toml.TomlMapper;
import com.google.common.io.Files;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;

public class ConfigurationUtils {

private static final Collection<DeserializationFeatureConfig> DESERIALIZATION_FEATURES =
List.of(
new DeserializationFeatureConfig(
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true),
new DeserializationFeatureConfig(DeserializationFeature.WRAP_EXCEPTIONS, true),
new DeserializationFeatureConfig(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true),
new DeserializationFeatureConfig(DeserializationFeature.USE_LONG_FOR_INTS, true),
new DeserializationFeatureConfig(
DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, false),
new DeserializationFeatureConfig(
DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false));

private static final JsonMapper JSON_MAPPER;
private static final TomlMapper TOML_MAPPER;

static {
JsonMapper.Builder builder = JsonMapper.builder();
DESERIALIZATION_FEATURES.forEach(f -> builder.configure(f.feature(), f.state()));
JSON_MAPPER = builder.build();
}

static {
TomlMapper.Builder builder = TomlMapper.builder();
DESERIALIZATION_FEATURES.forEach(f -> builder.configure(f.feature(), f.state()));
TOML_MAPPER = builder.build();
}

public static JsonMapper jsonMapper() {
return JSON_MAPPER;
}

public static TomlMapper tomlMapper() {
return TOML_MAPPER;
}

public static RootConfiguration loadConfigurationFile(Path file) throws IOException {
String extension = Files.getFileExtension(file.toString());
if (extension.equals("json")) {
return jsonMapper().readValue(file.toFile(), RootConfiguration.class);
} else if (extension.equals("toml")) {
return tomlMapper().readValue(file.toFile(), RootConfiguration.class);
}
throw new IOException(
"Unknown file extension '" + extension + "', extension must be either json or toml.");
}

private record DeserializationFeatureConfig(DeserializationFeature feature, boolean state) {}
}
Loading