Skip to content

Commit f492474

Browse files
Merge pull request quarkusio#47756 from phillip-kruger/chappie-extension-enablement
Introduce new assistant module
2 parents 137cb57 + c22663a commit f492474

File tree

38 files changed

+1055
-76
lines changed

38 files changed

+1055
-76
lines changed

bom/application/pom.xml

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,26 @@
605605
</dependency>
606606

607607
<!-- Quarkus libraries -->
608-
608+
<dependency>
609+
<groupId>io.quarkus</groupId>
610+
<artifactId>quarkus-assistant</artifactId>
611+
<version>${project.version}</version>
612+
</dependency>
613+
<dependency>
614+
<groupId>io.quarkus</groupId>
615+
<artifactId>quarkus-assistant-dev</artifactId>
616+
<version>${project.version}</version>
617+
</dependency>
618+
<dependency>
619+
<groupId>io.quarkus</groupId>
620+
<artifactId>quarkus-assistant-deployment</artifactId>
621+
<version>${project.version}</version>
622+
</dependency>
623+
<dependency>
624+
<groupId>io.quarkus</groupId>
625+
<artifactId>quarkus-assistant-deployment-spi</artifactId>
626+
<version>${project.version}</version>
627+
</dependency>
609628
<dependency>
610629
<groupId>io.quarkus</groupId>
611630
<artifactId>quarkus-caffeine</artifactId>
@@ -1917,6 +1936,7 @@
19171936
<artifactId>quarkus-smallrye-graphql</artifactId>
19181937
<version>${project.version}</version>
19191938
</dependency>
1939+
19201940
<dependency>
19211941
<groupId>io.quarkus</groupId>
19221942
<artifactId>quarkus-smallrye-graphql-deployment</artifactId>

core/deployment/src/main/java/io/quarkus/deployment/Capability.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ public interface Capability {
1212
*/
1313
String AGROAL = QUARKUS_PREFIX + ".agroal";
1414

15+
/**
16+
* An assistant implementation
17+
*/
18+
String ASSISTANT = QUARKUS_PREFIX + ".assistant";
19+
1520
/**
1621
* JSR 365 compatible contexts and dependency injection
1722
*/

core/devmode-spi/src/main/java/io/quarkus/dev/console/DevConsoleManager.java

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
import java.util.Map;
66
import java.util.NoSuchElementException;
77
import java.util.concurrent.ConcurrentHashMap;
8+
import java.util.function.BiFunction;
89
import java.util.function.Consumer;
910
import java.util.function.Function;
1011

1112
import io.quarkus.dev.spi.HotReplacementContext;
1213

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

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

3435
public static void sentRequest(DevConsoleRequest request) {
35-
Consumer<DevConsoleRequest> handler = DevConsoleManager.handler;
36-
if (handler == null) {
36+
Consumer<DevConsoleRequest> h = DevConsoleManager.handler;
37+
if (h == null) {
3738
request.getResponse().complete(new DevConsoleResponse(503, Collections.emptyMap(), new byte[0])); //service unavailable
3839
} else {
39-
handler.accept(request);
40+
h.accept(request);
4041
}
4142
}
4243

@@ -98,6 +99,7 @@ public static void close() {
9899
hotReplacementContext = null;
99100
quarkusBootstrap = null;
100101
actions.clear();
102+
assistantActions.clear();
101103
globals.clear();
102104
}
103105

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

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

125+
/**
126+
* Registers an action that will be called by a JSON RPC service at runtime, this action will include the assistant if
127+
* available
128+
*
129+
* @param name the name of the action, should be namespaced to avoid conflicts
130+
* @param action the action. The function receives a Map as parameters (named parameters) and returns an object of type
131+
* {@code T}.
132+
* Note that the type {@code T} must be a class shared by both the deployment and the runtime.
133+
*/
134+
public static <T> void register(String name, BiFunction<Object, Map<String, String>, T> action) {
135+
assistantActions.put(name, action);
136+
}
137+
122138
/**
123139
* Invokes a registered action
124140
*
@@ -141,7 +157,18 @@ public static <T> T invoke(String name) {
141157
public static <T> T invoke(String name, Map<String, String> params) {
142158
var function = actions.get(name);
143159
if (function == null) {
144-
throw new NoSuchElementException(name);
160+
// Try assistant actions
161+
var bifunction = assistantActions.get(name);
162+
if (bifunction != null) {
163+
Object assistant = DevConsoleManager.getGlobal(DEV_MANAGER_GLOBALS_ASSISTANT);
164+
if (assistant != null) {
165+
return (T) bifunction.apply(assistant, params);
166+
} else {
167+
throw new RuntimeException("Assistant not available");
168+
}
169+
} else {
170+
throw new NoSuchElementException(name);
171+
}
145172
} else {
146173
return (T) function.apply(params);
147174
}

devtools/bom-descriptor-json/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,19 @@
226226
</exclusion>
227227
</exclusions>
228228
</dependency>
229+
<dependency>
230+
<groupId>io.quarkus</groupId>
231+
<artifactId>quarkus-assistant</artifactId>
232+
<version>${project.version}</version>
233+
<type>pom</type>
234+
<scope>test</scope>
235+
<exclusions>
236+
<exclusion>
237+
<groupId>*</groupId>
238+
<artifactId>*</artifactId>
239+
</exclusion>
240+
</exclusions>
241+
</dependency>
229242
<dependency>
230243
<groupId>io.quarkus</groupId>
231244
<artifactId>quarkus-avro</artifactId>

docs/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,19 @@
235235
</exclusion>
236236
</exclusions>
237237
</dependency>
238+
<dependency>
239+
<groupId>io.quarkus</groupId>
240+
<artifactId>quarkus-assistant-deployment</artifactId>
241+
<version>${project.version}</version>
242+
<type>pom</type>
243+
<scope>test</scope>
244+
<exclusions>
245+
<exclusion>
246+
<groupId>*</groupId>
247+
<artifactId>*</artifactId>
248+
</exclusion>
249+
</exclusions>
250+
</dependency>
238251
<dependency>
239252
<groupId>io.quarkus</groupId>
240253
<artifactId>quarkus-avro-deployment</artifactId>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>quarkus-assistant-parent</artifactId>
7+
<groupId>io.quarkus</groupId>
8+
<version>999-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>quarkus-assistant-deployment-spi</artifactId>
13+
<name>Quarkus - Assistant - Deployment SPI</name>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>io.quarkus</groupId>
18+
<artifactId>quarkus-assistant-dev</artifactId>
19+
</dependency>
20+
<dependency>
21+
<groupId>io.quarkus</groupId>
22+
<artifactId>quarkus-core-deployment</artifactId>
23+
</dependency>
24+
<dependency>
25+
<groupId>io.quarkus</groupId>
26+
<artifactId>quarkus-vertx-http-dev-ui-spi</artifactId>
27+
</dependency>
28+
29+
30+
</dependencies>
31+
32+
</project>
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package io.quarkus.assistant.deployment.spi;
2+
3+
import java.util.Map;
4+
import java.util.Optional;
5+
import java.util.concurrent.CompletionStage;
6+
import java.util.function.Function;
7+
import java.util.function.Supplier;
8+
9+
import io.quarkus.assistant.runtime.dev.Assistant;
10+
import io.quarkus.builder.item.MultiBuildItem;
11+
import io.quarkus.deployment.console.ConsoleCommand;
12+
import io.quarkus.deployment.dev.testing.MessageFormat;
13+
14+
/**
15+
* Add a menu item in the Assistant console menu
16+
*
17+
* Extensions can produce this to add a item under the Assistant heading in the console
18+
* This will only appear in the console if the assistant is available
19+
*/
20+
public final class AssistantConsoleBuildItem extends MultiBuildItem {
21+
private final ConsoleCommand consoleCommand;
22+
23+
private final String description;
24+
private final char key;
25+
private final Optional<String> systemMessage;
26+
private final String userMessage;
27+
private final Supplier<String> colorSupplier;
28+
private final Supplier<String> stateSupplier;
29+
private final Map<String, String> variables;
30+
private final Optional<Function<Assistant, CompletionStage<?>>> function;
31+
32+
public AssistantConsoleBuildItem(ConsoleCommand consoleCommand) {
33+
this.consoleCommand = consoleCommand;
34+
this.function = Optional.empty();
35+
this.description = consoleCommand.getDescription();
36+
this.key = consoleCommand.getKey();
37+
this.systemMessage = Optional.empty();
38+
this.userMessage = null;
39+
this.colorSupplier = consoleCommand.getHelpState().getColorSupplier();
40+
this.stateSupplier = consoleCommand.getHelpState().getStateSupplier();
41+
this.variables = Map.of();
42+
}
43+
44+
private AssistantConsoleBuildItem(Builder builder) {
45+
this.description = builder.description;
46+
this.key = builder.key;
47+
this.systemMessage = builder.systemMessage;
48+
this.userMessage = builder.userMessage;
49+
this.colorSupplier = builder.colorSupplier;
50+
this.stateSupplier = builder.stateSupplier;
51+
this.variables = builder.variables;
52+
this.consoleCommand = null;
53+
this.function = builder.function;
54+
}
55+
56+
public static Builder builder() {
57+
return new Builder();
58+
}
59+
60+
public static class Builder {
61+
private String description;
62+
private char key = Character.MIN_VALUE;
63+
private Optional<String> systemMessage = Optional.empty();
64+
private String userMessage;
65+
private Supplier<String> colorSupplier = new Supplier<String>() {
66+
@Override
67+
public String get() {
68+
return MessageFormat.RESET;
69+
}
70+
};
71+
private Supplier<String> stateSupplier = null;
72+
private Map<String, String> variables = Map.of();
73+
private Optional<Function<Assistant, CompletionStage<?>>> function = Optional.empty();
74+
75+
public Builder description(String description) {
76+
this.description = description;
77+
return this;
78+
}
79+
80+
public Builder key(char key) {
81+
this.key = key;
82+
return this;
83+
}
84+
85+
public Builder systemMessage(String systemMessage) {
86+
this.systemMessage = Optional.of(systemMessage);
87+
return this;
88+
}
89+
90+
public Builder userMessage(String userMessage) {
91+
this.userMessage = userMessage;
92+
return this;
93+
}
94+
95+
public Builder colorSupplier(Supplier<String> colorSupplier) {
96+
this.colorSupplier = colorSupplier;
97+
return this;
98+
}
99+
100+
public Builder stateSupplier(Supplier<String> stateSupplier) {
101+
this.stateSupplier = stateSupplier;
102+
return this;
103+
}
104+
105+
public Builder variables(Map<String, String> variables) {
106+
this.variables = variables;
107+
return this;
108+
}
109+
110+
public Builder function(Function<Assistant, CompletionStage<?>> function) {
111+
this.function = Optional.of(function);
112+
return this;
113+
}
114+
115+
public AssistantConsoleBuildItem build() {
116+
if (key == Character.MIN_VALUE) {
117+
throw new IllegalStateException(
118+
"You have to specify a key. This is the key the user will press to get to your function");
119+
}
120+
if (description == null || description.isBlank()) {
121+
throw new IllegalStateException(
122+
"You have to specify a description. This is what the user will see in the console menu");
123+
}
124+
if (userMessage == null && !function.isPresent()) {
125+
throw new IllegalStateException(
126+
"You have to specify userMessage that will be send to AI, or implement your own using the function");
127+
}
128+
129+
return new AssistantConsoleBuildItem(this);
130+
}
131+
}
132+
133+
public ConsoleCommand getConsoleCommand() {
134+
return consoleCommand;
135+
}
136+
137+
public String getDescription() {
138+
return consoleCommand != null ? consoleCommand.getDescription() : description;
139+
}
140+
141+
public char getKey() {
142+
return key;
143+
}
144+
145+
public Optional<String> getSystemMessage() {
146+
return systemMessage;
147+
}
148+
149+
public String getUserMessage() {
150+
return userMessage;
151+
}
152+
153+
public Supplier<String> getColorSupplier() {
154+
return consoleCommand != null ? consoleCommand.getHelpState().getColorSupplier() : colorSupplier;
155+
}
156+
157+
public Supplier<String> getStateSupplier() {
158+
return consoleCommand != null ? consoleCommand.getHelpState().getStateSupplier() : stateSupplier;
159+
}
160+
161+
public Map<String, String> getVariables() {
162+
return variables;
163+
}
164+
165+
public Optional<Function<Assistant, CompletionStage<?>>> getFunction() {
166+
return function;
167+
}
168+
}

0 commit comments

Comments
 (0)