Skip to content
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
22 changes: 21 additions & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,26 @@
</dependency>

<!-- Quarkus libraries -->

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant-dev</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant-deployment</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant-deployment-spi</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-caffeine</artifactId>
Expand Down Expand Up @@ -1917,6 +1936,7 @@
<artifactId>quarkus-smallrye-graphql</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql-deployment</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ public interface Capability {
*/
String AGROAL = QUARKUS_PREFIX + ".agroal";

/**
* An assistant implementation
*/
String ASSISTANT = QUARKUS_PREFIX + ".assistant";

/**
* JSR 365 compatible contexts and dependency injection
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import io.quarkus.dev.spi.HotReplacementContext;

public class DevConsoleManager {

public static volatile String DEV_MANAGER_GLOBALS_ASSISTANT = "_assistant";
private static volatile Consumer<DevConsoleRequest> handler;
private static volatile Map<String, Map<String, Object>> templateInfo;
private static volatile HotReplacementContext hotReplacementContext;
Expand All @@ -25,18 +26,18 @@ public class DevConsoleManager {
* <p>
* As the class loaders are different these objects will generally need to implement some kind of common interface
*/
private static Map<String, Object> globals = new ConcurrentHashMap<>();
private static final Map<String, Object> globals = new ConcurrentHashMap<>();

public static void registerHandler(Consumer<DevConsoleRequest> requestHandler) {
handler = requestHandler;
}

public static void sentRequest(DevConsoleRequest request) {
Consumer<DevConsoleRequest> handler = DevConsoleManager.handler;
if (handler == null) {
Consumer<DevConsoleRequest> h = DevConsoleManager.handler;
if (h == null) {
request.getResponse().complete(new DevConsoleResponse(503, Collections.emptyMap(), new byte[0])); //service unavailable
} else {
handler.accept(request);
h.accept(request);
}
}

Expand Down Expand Up @@ -98,6 +99,7 @@ public static void close() {
hotReplacementContext = null;
quarkusBootstrap = null;
actions.clear();
assistantActions.clear();
globals.clear();
}

Expand All @@ -106,6 +108,7 @@ public static void close() {
* The action registered here should be used with the Dev UI / JSON RPC services.
*/
private static final Map<String, Function<Map<String, String>, ?>> actions = new HashMap<>();
private static final Map<String, BiFunction<Object, Map<String, String>, ?>> assistantActions = new HashMap<>();

/**
* Registers an action that will be called by a JSON RPC service at runtime
Expand All @@ -119,6 +122,19 @@ public static <T> void register(String name, Function<Map<String, String>, T> ac
actions.put(name, action);
}

/**
* Registers an action that will be called by a JSON RPC service at runtime, this action will include the assistant if
* available
*
* @param name the name of the action, should be namespaced to avoid conflicts
* @param action the action. The function receives a Map as parameters (named parameters) and returns an object of type
* {@code T}.
* Note that the type {@code T} must be a class shared by both the deployment and the runtime.
*/
public static <T> void register(String name, BiFunction<Object, Map<String, String>, T> action) {
assistantActions.put(name, action);
}

/**
* Invokes a registered action
*
Expand All @@ -141,7 +157,18 @@ public static <T> T invoke(String name) {
public static <T> T invoke(String name, Map<String, String> params) {
var function = actions.get(name);
if (function == null) {
throw new NoSuchElementException(name);
// Try assistant actions
var bifunction = assistantActions.get(name);
if (bifunction != null) {
Object assistant = DevConsoleManager.getGlobal(DEV_MANAGER_GLOBALS_ASSISTANT);
if (assistant != null) {
return (T) bifunction.apply(assistant, params);
} else {
throw new RuntimeException("Assistant not available");
}
} else {
throw new NoSuchElementException(name);
}
} else {
return (T) function.apply(params);
}
Expand Down
13 changes: 13 additions & 0 deletions devtools/bom-descriptor-json/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-avro</artifactId>
Expand Down
13 changes: 13 additions & 0 deletions docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-avro-deployment</artifactId>
Expand Down
32 changes: 32 additions & 0 deletions extensions/assistant/deployment-spi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-assistant-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-assistant-deployment-spi</artifactId>
<name>Quarkus - Assistant - Deployment SPI</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-assistant-dev</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-core-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-vertx-http-dev-ui-spi</artifactId>
</dependency>


</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package io.quarkus.assistant.deployment.spi;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.function.Supplier;

import io.quarkus.assistant.runtime.dev.Assistant;
import io.quarkus.builder.item.MultiBuildItem;
import io.quarkus.deployment.console.ConsoleCommand;
import io.quarkus.deployment.dev.testing.MessageFormat;

/**
* Add a menu item in the Assistant console menu
*
* Extensions can produce this to add a item under the Assistant heading in the console
* This will only appear in the console if the assistant is available
*/
public final class AssistantConsoleBuildItem extends MultiBuildItem {
private final ConsoleCommand consoleCommand;

private final String description;
private final char key;
private final Optional<String> systemMessage;
private final String userMessage;
private final Supplier<String> colorSupplier;
private final Supplier<String> stateSupplier;
private final Map<String, String> variables;
private final Optional<Function<Assistant, CompletionStage<?>>> function;

public AssistantConsoleBuildItem(ConsoleCommand consoleCommand) {
this.consoleCommand = consoleCommand;
this.function = Optional.empty();
this.description = consoleCommand.getDescription();
this.key = consoleCommand.getKey();
this.systemMessage = Optional.empty();
this.userMessage = null;
this.colorSupplier = consoleCommand.getHelpState().getColorSupplier();
this.stateSupplier = consoleCommand.getHelpState().getStateSupplier();
this.variables = Map.of();
}

private AssistantConsoleBuildItem(Builder builder) {
this.description = builder.description;
this.key = builder.key;
this.systemMessage = builder.systemMessage;
this.userMessage = builder.userMessage;
this.colorSupplier = builder.colorSupplier;
this.stateSupplier = builder.stateSupplier;
this.variables = builder.variables;
this.consoleCommand = null;
this.function = builder.function;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private String description;
private char key = Character.MIN_VALUE;
private Optional<String> systemMessage = Optional.empty();
private String userMessage;
private Supplier<String> colorSupplier = new Supplier<String>() {
@Override
public String get() {
return MessageFormat.RESET;
}
};
private Supplier<String> stateSupplier = null;
private Map<String, String> variables = Map.of();
private Optional<Function<Assistant, CompletionStage<?>>> function = Optional.empty();

public Builder description(String description) {
this.description = description;
return this;
}

public Builder key(char key) {
this.key = key;
return this;
}

public Builder systemMessage(String systemMessage) {
this.systemMessage = Optional.of(systemMessage);
return this;
}

public Builder userMessage(String userMessage) {
this.userMessage = userMessage;
return this;
}

public Builder colorSupplier(Supplier<String> colorSupplier) {
this.colorSupplier = colorSupplier;
return this;
}

public Builder stateSupplier(Supplier<String> stateSupplier) {
this.stateSupplier = stateSupplier;
return this;
}

public Builder variables(Map<String, String> variables) {
this.variables = variables;
return this;
}

public Builder function(Function<Assistant, CompletionStage<?>> function) {
this.function = Optional.of(function);
return this;
}

public AssistantConsoleBuildItem build() {
if (key == Character.MIN_VALUE) {
throw new IllegalStateException(
"You have to specify a key. This is the key the user will press to get to your function");
}
if (description == null || description.isBlank()) {
throw new IllegalStateException(
"You have to specify a description. This is what the user will see in the console menu");
}
if (userMessage == null && !function.isPresent()) {
throw new IllegalStateException(
"You have to specify userMessage that will be send to AI, or implement your own using the function");
}

return new AssistantConsoleBuildItem(this);
}
}

public ConsoleCommand getConsoleCommand() {
return consoleCommand;
}

public String getDescription() {
return consoleCommand != null ? consoleCommand.getDescription() : description;
}

public char getKey() {
return key;
}

public Optional<String> getSystemMessage() {
return systemMessage;
}

public String getUserMessage() {
return userMessage;
}

public Supplier<String> getColorSupplier() {
return consoleCommand != null ? consoleCommand.getHelpState().getColorSupplier() : colorSupplier;
}

public Supplier<String> getStateSupplier() {
return consoleCommand != null ? consoleCommand.getHelpState().getStateSupplier() : stateSupplier;
}

public Map<String, String> getVariables() {
return variables;
}

public Optional<Function<Assistant, CompletionStage<?>>> getFunction() {
return function;
}
}
Loading
Loading