chore: Add README.md files

This commit is contained in:
Roque Caballero 2025-03-28 17:36:22 +01:00
commit 4ceec902a3
Signed by: roque.caballero
SSH key fingerprint: SHA256:+oco2mi9KAXp5fmBGQyUMk3bBo0scA4b8sL7Gf2pEwo
155 changed files with 19124 additions and 0 deletions

5
demo-04/README.md Normal file
View file

@ -0,0 +1,5 @@
Demo 04 - System messages
===============================================
A system message in a LLM is a directive that helps guide the models behavior and tone during an interaction.
It typically sets the context, role, or boundaries for the model, defining how it should respond to the user.

199
demo-04/pom.xml Normal file
View file

@ -0,0 +1,199 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ch.phoenixtechnologies</groupId>
<artifactId>ai-lc4j-workshop</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>demo-04</artifactId>
<properties>
<java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!-- Plugin versions -->
<compiler-plugin.version>3.14.0</compiler-plugin.version>
<enforcer-plugin.version>3.5.0</enforcer-plugin.version>
<surefire-plugin.version>3.5.2</surefire-plugin.version>
<spotless-maven-plugin.version>2.44.3</spotless-maven-plugin.version>
<palantir-java-format.version>2.50.0</palantir-java-format.version>
<release-plugin.version>3.1.1</release-plugin.version>
<!-- Quarkus version -->
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>3.19.2</quarkus.platform.version>
<!-- LangChain4j-->
<quarkus-langchain4j.version>0.25.0</quarkus-langchain4j.version>
<!-- Test dependency versions -->
<assertj.version>3.27.3</assertj.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-bom</artifactId>
<version>${quarkus-langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkiverse.langchain4j</groupId>
<artifactId>quarkus-langchain4j-openai</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets-next</artifactId>
</dependency>
<!-- UI -->
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>importmap</artifactId>
<version>1.0.8</version>
</dependency>
<dependency>
<groupId>org.mvnpm.at.mvnpm</groupId>
<artifactId>vaadin-webcomponents</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>es-module-shims</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mvnpm</groupId>
<artifactId>wc-chatbot</artifactId>
<version>0.2.0</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<release>${java.version}</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-enforcer-plugin</artifactId>
<version>${enforcer-plugin.version}</version>
<executions>
<execution>
<id>ban-bad-log4j-versions</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>org.apache.logging.log4j:log4j-core:(,2.17.1)</exclude>
</excludes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>${spotless-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
<phase>compile</phase>
</execution>
</executions>
<configuration>
<java>
<removeUnusedImports />
<palantirJavaFormat>
<version>${palantir-java-format.version}</version>
<style>PALANTIR</style>
<formatJavadoc>false</formatJavadoc>
</palantirJavaFormat>
</java>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>${release-plugin.version}</version>
<configuration>
<tagNameFormat>@{project.version}</tagNameFormat>
<checkModificationExcludes>mvnw</checkModificationExcludes>
<scmReleaseCommitComment>chore: release @{releaseLabel}</scmReleaseCommitComment>
<scmDevelopmentCommitComment>chore: prepare for next development iteration [skip ci]</scmDevelopmentCommitComment>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,18 @@
package ch.phoenixtechnologies.lc4j.workshop;
import dev.langchain4j.service.SystemMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.smallrye.mutiny.Multi;
import jakarta.enterprise.context.SessionScoped;
@SessionScoped
@RegisterAiService
public interface CustomerSupportAgent {
@SystemMessage(
"""
You are a customer support agent of a IT company 'Phoenix Technologies AG based in Switzerland.
You are friendly, polite and concise.
If the question is unrelated to IT services, you should politely redirect the customer to the right department.
""")
Multi<String> chat(String userMessage);
}

View file

@ -0,0 +1,25 @@
package ch.phoenixtechnologies.lc4j.workshop;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.smallrye.mutiny.Multi;
@WebSocket(path = "/customer-support-agent")
public class CustomerSupportAgentWebSocket {
private final CustomerSupportAgent agent;
public CustomerSupportAgentWebSocket(CustomerSupportAgent agent) {
this.agent = agent;
}
@OnOpen
public String onOpen() {
return "Welcome to Phoenix Technologies AI!";
}
@OnTextMessage
public Multi<String> onTextMessage(String message) {
return agent.chat(message);
}
}

View file

@ -0,0 +1,45 @@
package ch.phoenixtechnologies.lc4j.workshop;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import org.mvnpm.importmap.Aggregator;
@ApplicationScoped
@Path("/_importmap")
public class ImportmapResource {
private static final String JAVASCRIPT_CODE =
"""
const im = document.createElement('script');
im.type = 'importmap';
im.textContent = JSON.stringify(%s);
document.currentScript.after(im);
""";
private String importmap;
@PostConstruct
void init() {
Aggregator aggregator = new Aggregator();
// Add our own mappings
aggregator.addMapping("icons/", "/icons/");
aggregator.addMapping("components/", "/components/");
aggregator.addMapping("fonts/", "/fonts/");
this.importmap = aggregator.aggregateAsJson();
}
@GET
@Path("/dynamic.importmap")
@Produces("application/importmap+json")
public String importMap() {
return this.importmap;
}
@GET
@Path("/dynamic-importmap.js")
@Produces("application/javascript")
public String importMapJson() {
return JAVASCRIPT_CODE.formatted(this.importmap);
}
}

View file

@ -0,0 +1,74 @@
import {css, LitElement} from 'lit';
import '@vaadin/icon';
import '@vaadin/button';
import '@vaadin/text-field';
import '@vaadin/text-area';
import '@vaadin/form-layout';
import '@vaadin/progress-bar';
import '@vaadin/checkbox';
import '@vaadin/horizontal-layout';
import '@vaadin/grid';
import '@vaadin/grid/vaadin-grid-sort-column.js';
export class DemoChat extends LitElement {
_stripHtml(html) {
const div = document.createElement("div");
div.innerHTML = html;
return div.textContent || div.innerText || "";
}
connectedCallback() {
const chatBot = document.getElementsByTagName("chat-bot")[0];
const protocol = (window.location.protocol === 'https:') ? 'wss' : 'ws';
const socket = new WebSocket(protocol + '://' + window.location.host + '/customer-support-agent');
const that = this;
socket.onmessage = function (event) {
chatBot.hideLastLoading();
// LLM response
let lastMessage;
if (chatBot.messages.length > 0) {
lastMessage = chatBot.messages[chatBot.messages.length - 1];
}
if (lastMessage && lastMessage.sender.name === "Bot" && ! lastMessage.loading) {
if (! lastMessage.msg) {
lastMessage.msg = "";
}
lastMessage.msg += event.data;
let bubbles = chatBot.shadowRoot.querySelectorAll("chat-bubble");
let bubble = bubbles.item(bubbles.length - 1);
if (lastMessage.message) {
bubble.innerHTML = that._stripHtml(lastMessage.message) + lastMessage.msg;
} else {
bubble.innerHTML = lastMessage.msg;
}
chatBot.body.scrollTo({ top: chatBot.body.scrollHeight, behavior: 'smooth' })
} else {
chatBot.sendMessage(event.data, {
right: false,
sender: {
name: "Bot"
}
});
}
}
chatBot.addEventListener("sent", function (e) {
if (e.detail.message.sender.name !== "Bot") {
// User message
const msg = that._stripHtml(e.detail.message.message);
socket.send(msg);
chatBot.sendMessage("", {
right: false,
loading: true
});
}
});
}
}
customElements.define('demo-chat', DemoChat);

View file

@ -0,0 +1,63 @@
import {LitElement, html, css} from 'lit';
import '@vaadin/icon';
import '@vaadin/button';
import '@vaadin/text-field';
import '@vaadin/text-area';
import '@vaadin/form-layout';
import '@vaadin/progress-bar';
import '@vaadin/checkbox';
import '@vaadin/grid';
import '@vaadin/grid/vaadin-grid-sort-column.js';
export class DemoTitle extends LitElement {
static styles = css`
h2 {
font-family: "Red Hat Mono", monospace;
font-size: 60px;
font-style: normal;
font-variant: normal;
font-weight: 700;
line-height: 26.4px;
color: var(--main-highlight-text-color);
}
.title {
text-align: center;
padding: 1em;
background: var(--main-bg-color);
}
.explanation {
margin-left: auto;
margin-right: auto;
width: 50%;
text-align: justify;
font-size: 20px;
}
.explanation img {
max-width: 60%;
display: block;
float:left;
margin-right: 2em;
margin-top: 1em;
}
`
render() {
return html`
<div class="title">
<h2>Phoenix Technologies</h2>
</div>
<div class="explanation">
<p>Welcome to Phoenix Technologies!</p>
<p>Please click the button on the bottom right to start the conversation
with an LLM-powered customer support agent.</p>
</div>
`
}
}
customElements.define('demo-title', DemoTitle);

View file

@ -0,0 +1 @@
@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg8z6hR4jNCH5Z.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Display";font-style:normal;font-weight:700;font-display:swap;src:url(https://fonts.gstatic.com/s/redhatdisplay/v7/8vIQ7wUr0m80wwYf0QCXZzYzUoTg_T6hR4jNCA.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQZqctMc-JPWCN.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:400;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQaKctMc-JPQ.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQZqctMc-JPWCN.woff2) format("woff2");unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:"Red Hat Text";font-style:normal;font-weight:500;font-display:swap;src:url(https://fonts.gstatic.com/s/redhattext/v6/RrQXbohi_ic6B3yVSzGBrMxQaKctMc-JPQ.woff2) format("woff2");unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}/*# sourceMappingURL=red-hat-font.css.map */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
// import './font-awesome-brands.js';
// import './font-awesome-regular.js';
import './font-awesome-solid.js';
// export * from './font-awesome-brands.js';
// export * from './font-awesome-regular.js';
export * from './font-awesome-solid.js';

View file

@ -0,0 +1,82 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" type="image/png" href="favicon.ico">
<script src="/_importmap/dynamic-importmap.js"></script>
<script type="module">
import 'icons/font-awesome.js';
import 'components/demo-title.js';
import 'components/demo-chat.js';
import 'wc-chatbot';
</script>
<link rel="stylesheet" href="fonts/red-hat-font.min.css">
<title>Miles of Smiles</title>
<style>
:root {
--main-bg-color: rgb(246, 242, 242);
--main-highlight-text-color: rgba(237, 98, 128);
}
body {
margin: 0;
width: 100%;
height: 100vh;
font-family: 'Red Hat Text', sans-serif;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: center;
color: var(--lumo-body-text-color);
background: var(--main-bg-color);
}
chat-bot {
--chatbot-avatar-bg-color: rgba(237, 98, 128);
--chatbot-avatar-margin: 10%;
--chatbot-header-bg-color: rgba(237, 98, 128);
--chatbot-header-title-color: #FFFFFF;
--chatbot-body-bg-color: var(--main-bg-color);
--chatbot-send-button-color: rgba(237, 98, 128);
}
chat-bot::part(chat-bubble) {
--chat-bubble-avatar-color: rgba(237, 98, 128);
--chat-bubble-color: rgba(203, 232, 237, 0.71);
--chat-bubble-right-color: rgb(157, 238, 244);
--chat-bubble-font-color: #333;
--chat-bubble-font-right-color: #333;
--chat-bubble-delay: 0.2s;
}
.middle {
margin-top: 2em;
overflow: hidden;
width: 50%;
margin-left: auto;
margin-right: auto;
display: flex;
}
</style>
</head>
<body>
<demo-title></demo-title>
<div class="middle">
<demo-chat>
<chat-bot></chat-bot>
</demo-chat>
</div>
</body>
</html>

View file

@ -0,0 +1,14 @@
quarkus:
langchain4j:
openai:
api-key: #PUT_YOUR_TOKEN_HERE
base-url: https://inference-llama33-70b-maas.apps.ai.kvant.cloud/v1/
timeout: 60s
chat-model:
model-name: inference-llama33-70b
temperature: 1.0
#max-tokens: 1000
#frequency-penalty: 2
frequency-penalty: 0
log-requests: true
log-responses: true