Skip to content

Commit

Permalink
get full config reading working from file
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterjackson committed Sep 8, 2023
1 parent 4a73548 commit a3ced81
Show file tree
Hide file tree
Showing 25 changed files with 1,189 additions and 182 deletions.
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.

13 changes: 13 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<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 @@ -48,6 +49,18 @@
<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>
Expand Down
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
Expand Up @@ -11,6 +11,9 @@
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;

Expand Down Expand Up @@ -51,5 +54,16 @@ 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) {}
}
115 changes: 111 additions & 4 deletions src/main/java/com/meta/chatbridge/configuration/RootConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,126 @@

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.meta.chatbridge.Service;
import com.meta.chatbridge.ServiceConfiguration;
import com.meta.chatbridge.ServicesRunner;
import com.meta.chatbridge.llm.LLMConfig;
import com.meta.chatbridge.llm.LLMPlugin;
import com.meta.chatbridge.message.HandlerConfig;
import com.meta.chatbridge.message.Message;
import com.meta.chatbridge.message.MessageHandler;
import com.meta.chatbridge.store.ChatStore;
import com.meta.chatbridge.store.StoreConfig;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.checkerframework.checker.nullness.qual.Nullable;

public class RootConfiguration {
private final Collection<LLMConfig> plugins;
private final Map<String, LLMConfig> plugins;
private final Map<String, StoreConfig> stores;
private final Map<String, HandlerConfig> handlers;
private final Collection<ServiceConfiguration> services;

private final int port;

@JsonCreator
public RootConfiguration(@JsonProperty("plugins") Collection<LLMConfig> plugins) {
this.plugins = plugins;
RootConfiguration(
@JsonProperty("plugins") Collection<LLMConfig> plugins,
@JsonProperty("stores") Collection<StoreConfig> stores,
@JsonProperty("handlers") Collection<HandlerConfig> handlers,
@JsonProperty("services") Collection<ServiceConfiguration> services,
@JsonProperty("port") @Nullable Integer port) {
this.port = port == null ? 8080 : port;
Preconditions.checkArgument(
this.port >= 0 && this.port <= 65535, "port must be between 0 and 65535");

Preconditions.checkArgument(
plugins != null && !plugins.isEmpty(), "At least one plugin must defined");
Preconditions.checkArgument(
stores != null && !stores.isEmpty(), "at least one store must be defined");
Preconditions.checkArgument(
handlers != null && !handlers.isEmpty(), "at least one handler must be defined");
Preconditions.checkArgument(
services != null && !services.isEmpty(), "at least one service must be defined");

Preconditions.checkArgument(
plugins.size()
== plugins.stream().map(LLMConfig::name).collect(Collectors.toUnmodifiableSet()).size(),
"all plugin names must be unique");
this.plugins =
plugins.stream()
.collect(Collectors.toUnmodifiableMap(LLMConfig::name, Function.identity()));

Preconditions.checkArgument(
stores.size()
== stores.stream()
.map(StoreConfig::name)
.collect(Collectors.toUnmodifiableSet())
.size(),
"all store names must be unique");
this.stores =
stores.stream()
.collect(Collectors.toUnmodifiableMap(StoreConfig::name, Function.identity()));

Preconditions.checkArgument(
handlers.size()
== handlers.stream()
.map(HandlerConfig::name)
.collect(Collectors.toUnmodifiableSet())
.size(),
"all handler names must be unique");
this.handlers =
handlers.stream()
.collect(Collectors.toUnmodifiableMap(HandlerConfig::name, Function.identity()));

for (ServiceConfiguration s : services) {
Preconditions.checkArgument(
this.plugins.containsKey(s.plugin()), s.plugin() + " must be the name of a plugin");
Preconditions.checkArgument(
this.stores.containsKey(s.store()), s.store() + " must be the name of a store");
Preconditions.checkArgument(
this.handlers.containsKey(s.handler()), s.handler() + " must be the name of a handler");
}
this.services = services;
}

Collection<LLMConfig> plugins() {
return Collections.unmodifiableCollection(plugins);
return Collections.unmodifiableCollection(plugins.values());
}

Collection<StoreConfig> stores() {
return Collections.unmodifiableCollection(stores.values());
}

Collection<HandlerConfig> handlers() {
return Collections.unmodifiableCollection(handlers.values());
}

Collection<ServiceConfiguration> services() {
return Collections.unmodifiableCollection(services);
}

public int port() {
return port;
}

private <T extends Message> Service<T> createService(
MessageHandler<T> handler, ServiceConfiguration serviceConfig) {
LLMPlugin<T> plugin = plugins.get(serviceConfig.plugin()).toPlugin();
ChatStore<T> store = stores.get(serviceConfig.store()).toStore();
return new Service<>(store, handler, plugin, serviceConfig.webhookPath());
}

public ServicesRunner toServicesRunner() {
ServicesRunner runner = ServicesRunner.newInstance().port(port);
for (ServiceConfiguration service : services) {
MessageHandler<?> handler = handlers.get(service.handler()).toMessageHandler();
runner.service(createService(handler, service));
}
return runner;
}
}
3 changes: 2 additions & 1 deletion src/main/java/com/meta/chatbridge/llm/LLMConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.meta.chatbridge.message.Message;

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
Expand All @@ -19,5 +20,5 @@ public interface LLMConfig {

String name();

LLMPlugin<?> plugin();
<T extends Message> LLMPlugin<T> toPlugin();
}
Loading

0 comments on commit a3ced81

Please sign in to comment.