Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9a5a891
support AI
gkwan-ibm Aug 8, 2025
c8d2175
support AI
gkwan-ibm Aug 8, 2025
b515d80
support AI
gkwan-ibm Aug 11, 2025
9aaf850
support AI
gkwan-ibm Aug 11, 2025
b8284e9
update rag
gkwan-ibm Aug 11, 2025
42a913b
organized the import statements
gkwan-ibm Aug 12, 2025
f4f200d
Improve multi-line mode
GeeTransit Aug 14, 2025
e33ed95
Merge pull request #1 from gkwan-ibm/gee-patch-22
gkwan-ibm Aug 14, 2025
836e87a
Sync with prototype (#2)
AndrewSasmito Aug 14, 2025
6ec7f63
update security md file
gkwan-ibm Aug 14, 2025
8ca5425
add license comment
gkwan-ibm Aug 14, 2025
0f723f6
Patch to get the actual link in OpenLiberty blogs (#3)
AndrewSasmito Aug 14, 2025
368f280
sync the main branch
gkwan-ibm Aug 15, 2025
f9f06bb
add the help for view previous message
gkwan-ibm Aug 15, 2025
50e0dc4
Add javadoc (#4)
AndrewSasmito Aug 15, 2025
8a0fa0b
Add unit test functionality (#5)
AndrewSasmito Aug 18, 2025
6fe109f
sync with the prototype
gkwan-ibm Aug 18, 2025
35c65f9
change embedding store and print reply header and footer
gkwan-ibm Aug 19, 2025
0b80eba
print reply header and footer
gkwan-ibm Aug 19, 2025
fe29fb5
sync with prototype
gkwan-ibm Aug 19, 2025
45f99d5
fix for Windows
gkwan-ibm Aug 19, 2025
0e9649a
fix windows
gkwan-ibm Aug 19, 2025
5c86264
Support deleting across lines in multi-line input (#7)
GeeTransit Aug 21, 2025
3034e35
polish the reset message
gkwan-ibm Aug 21, 2025
2f70b1d
Ported changes (#8)
AndrewSasmito Aug 22, 2025
fbc71d1
Allow user to toggle AI (#6)
AndrewSasmito Aug 22, 2025
b0a2b59
prompt users to select AI provider
gkwan-ibm Aug 25, 2025
7168cb7
sync with main branch and reduce a skip message for AI
gkwan-ibm Aug 25, 2025
4872413
sync with main
gkwan-ibm Sep 15, 2025
16fd788
liberty features utils
gkwan-ibm Sep 15, 2025
9750c62
Move multiple file selection into tool and filter out target/ files (#9)
GeeTransit Sep 15, 2025
f80946e
sort features
gkwan-ibm Sep 16, 2025
cba00cf
sort features
gkwan-ibm Sep 16, 2025
d196c2c
handle null ai response
gkwan-ibm Sep 17, 2025
40fa8b9
Update the program to the newest dependencies (#13)
AndrewSasmito Oct 6, 2025
f0c030f
sync with main
gkwan-ibm Oct 16, 2025
31d6f70
sync with main
gkwan-ibm Oct 16, 2025
c78c0a4
sync with main branch
gkwan-ibm Oct 16, 2025
2603c6f
liberty config tool
gkwan-ibm Oct 24, 2025
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
82 changes: 79 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>io.openliberty.tools</groupId>
<artifactId>ci.common</artifactId>
<version>1.8.41-SNAPSHOT</version>
<version>2.0.0-AI-SNAPSHOT</version>
<packaging>jar</packaging>

<name>ci.common</name>
Expand Down Expand Up @@ -65,8 +65,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.javadoc.failOnError>false</maven.javadoc.failOnError>
</properties>

Expand Down Expand Up @@ -98,6 +98,82 @@
<artifactId>jackson-core</artifactId>
<version>2.19.2</version>
</dependency>
<!-- AI dependencies -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-github-models</artifactId>
<version>1.7.1-beta14</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-google-ai-gemini</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mistral-ai</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-agentic</artifactId>
<version>1.7.1-beta14</version>
</dependency>
<dependency>
<groupId>com.github.javaparser</groupId>
<artifactId>javaparser-core</artifactId>
<version>3.27.0</version>
</dependency>
<dependency>
<groupId>io.github.ollama4j</groupId>
<artifactId>ollama4j</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.25.1</version>
</dependency>
<dependency>
<groupId>org.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.25.1</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-json-binding-provider</artifactId>
<version>6.2.12.Final</version>
</dependency>
<dependency>
<groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.30.5</version>
</dependency>
</dependencies>

<build>
Expand Down
146 changes: 146 additions & 0 deletions src/main/java/io/openliberty/tools/common/ai/ChatAgent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* (C) Copyright IBM Corporation 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openliberty.tools.common.ai;

import dev.langchain4j.data.message.ToolExecutionResultMessage;
import dev.langchain4j.exception.InvalidRequestException;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.service.AiServices;
import io.openliberty.tools.common.ai.tools.CodingTools;
import io.openliberty.tools.common.ai.tools.OpenLibertyTools;
import io.openliberty.tools.common.ai.tools.StackOverFlowTools;
import io.openliberty.tools.common.ai.util.Assistant;
import io.openliberty.tools.common.ai.util.MarkdownConsoleFormatter;
import io.openliberty.tools.common.ai.util.ModelBuilder;
import io.openliberty.tools.common.ai.util.RagCreator;
import io.openliberty.tools.common.ai.util.Utils;

public class ChatAgent {
private ModelBuilder modelBuilder = new ModelBuilder();

private CodingTools codingTools;
private StackOverFlowTools stackOverFlowTools = new StackOverFlowTools();
private OpenLibertyTools openLibertyTools = new OpenLibertyTools();

private MarkdownConsoleFormatter mdFormatter = new MarkdownConsoleFormatter();

private Assistant assistant = null;

private int memoryId;

private boolean toolsEnabled = false;

public ChatAgent(int memoryId) throws Exception {
this(memoryId, new CodingTools());
}

public ChatAgent(int memoryId, CodingTools codingTools) throws Exception {
this.memoryId = memoryId;
this.codingTools = codingTools;
getAssistant();
}

public Assistant getAssistant() throws Exception {
if (assistant == null) {
AiServices<Assistant> builder =
AiServices.builder(Assistant.class)
.chatModel(modelBuilder.getChatModel())
.tools(stackOverFlowTools, codingTools, openLibertyTools)
.hallucinatedToolNameStrategy(
toolExecutionRequest -> ToolExecutionResultMessage.from(toolExecutionRequest,
"Error: there is no tool with the following parameters called "
+ toolExecutionRequest.name()))
.chatMemoryProvider(
sessionId -> MessageWindowChatMemory.withMaxMessages(modelBuilder.getMaxMessages()));
RagCreator creator = new RagCreator();
RetrievalAugmentor retrivalAugmentator = creator.getRetrievalAugmentor(modelBuilder.getEmbeddingModel());
if (retrivalAugmentator == null) {
System.out.println("[WARNING] RAG is not set up successfully. Continuing without RAG.");
} else {
builder.retrievalAugmentor(retrivalAugmentator);
}
assistant = builder.build();

try {
assistant.chat(memoryId, "test");
toolsEnabled = true;
} catch (InvalidRequestException e) {
toolsEnabled = false;
if (e.getMessage().contains("does not support tools")) {
System.out.println("WARNING: AI model " +
modelBuilder.getModelName() + " does not support tools");
builder = AiServices.builder(Assistant.class)
.chatModel(modelBuilder.getChatModel())
.chatMemoryProvider(
sessionId -> MessageWindowChatMemory.withMaxMessages(modelBuilder.getMaxMessages()));
if (retrivalAugmentator != null) {
builder.retrievalAugmentor(retrivalAugmentator);
}
assistant = builder.build();
} else {
throw new Exception(e);
}
} finally {
resetChat();
}
}
return assistant;
}

public String chat(String message) throws Exception {
if (message.equalsIgnoreCase("reset")) {
resetChat();
return "The current chat session is reset.\n";
} else {
String response = getAssistant().chat(memoryId, message).content();
if (response == null || response.isBlank()) {
return "AI reponsonded with nothing. Try your message again or a new message.\n";
}
return mdFormatter.rerender(response.trim());
}
}

public void resetChat() {
assistant.evictChatMemory(memoryId);
Utils.clearPermissions();
}

public String getModelName() {
return modelBuilder.getModelName();
}

public String getProvider() {
return modelBuilder.getProvider();
}

public String getToolsEnabled() {
return toolsEnabled ? "enabled" : "unavailable";
}

public Integer getTimeOut() {
return modelBuilder.getTimeOut();
}

public Integer getMaxNewToken() {
return modelBuilder.getMaxNewToken();
}

public Double getTemperature() {
return modelBuilder.getTemperature();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* (C) Copyright IBM Corporation 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openliberty.tools.common.ai.agents;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface ConfigureServerXmlAgent {

@Agent("Construct a Liberty configuration definition in xml format")
@SystemMessage("""
You construct the configuration xml element of the Liberty feature {{configurationName}}
for the user provided server.xml content,
simply use the xml from the following Liberty configuration document without adding any comment
and include the explanation of the attributes
by using the following Liberty configuration document:
{{configurationDoc}}
""")
@UserMessage("""
Here is the content of my server.xml:
{{fileContent}}
""")
public String addConfiguration(@V("configurationName") String configurationName, @V("configurationDoc") String configurationDoc, @V("fileContent") String fileContent);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* (C) Copyright IBM Corporation 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openliberty.tools.common.ai.agents;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.V;

public class ReadConfigrationDocAgent {

@Agent(description = "Read Liberty configuration document", outputName = "configurationDoc")
public String readConfigrationDoc(@V("configurationName") String configurationName) {
try {
if (configurationName.contains("-")) {
configurationName = configurationName.substring(0, configurationName.indexOf("-"));
}
URI configurationUri = new URI("https://raw.githubusercontent.com/OpenLiberty/docs-generated/refs/heads/vNext/modules/reference/pages/config/" + configurationName + ".adoc");
URL url = configurationUri.toURL();
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuffer sb = new StringBuffer();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
return sb.toString();
} catch (Exception e) {
System.err.println("ReadConfigrationDocAgent exception: " + e.getMessage());
return "";
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* (C) Copyright IBM Corporation 2025
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.openliberty.tools.common.ai.agents;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.V;
import io.openliberty.tools.common.ai.util.LibertyServerConfigurationUtil;

public class ReadConfigrationTemplateAgent {

@Agent(description = "Read Liberty configuration document", outputName = "configurationDoc")
public String readConfigrationDoc(@V("configurationName") String configurationName) {
if (configurationName.contains("-")) {
configurationName = configurationName.substring(0, configurationName.indexOf("-"));
}
StringBuffer sb = new StringBuffer();
sb.append(LibertyServerConfigurationUtil.getConfigurationTemplate(configurationName))
.append(LibertyServerConfigurationUtil.getConfigurationAttributes(configurationName));
return sb.toString();
}

}
Loading
Loading