Skip to content

Commit 55c9c69

Browse files
sberyozkinjmartisk
authored andcommitted
Add a Secure SQL ChatBot demo
1 parent 106d259 commit 55c9c69

34 files changed

+1482
-1
lines changed

samples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<module>review-triage</module>
2121
<module>secure-fraud-detection</module>
2222
<module>secure-poem-multiple-models</module>
23+
<module>secure-sql-chatbot</module>
2324
<module>sql-chatbot</module>
2425
</modules>
2526

samples/secure-fraud-detection/src/main/java/io/quarkiverse/langchain4j/sample/LogoutResource.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,6 @@ public Response logout(@Context UriInfo uriInfo) {
2424
// remove the local session cookie
2525
session.logout().await().indefinitely();
2626
// redirect to the login page
27-
return Response.seeOther(uriInfo.getBaseUriBuilder().path("login").build()).build();
27+
return Response.seeOther(uriInfo.getBaseUriBuilder().build()).build();
2828
}
2929
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
# Secure chatbot using advanced RAG and a SQL database
2+
3+
## Secure Movie Muse
4+
5+
This example demonstrates how to create a secure chatbot with RAG using
6+
`quarkus-langchain4j`. This chatbot internally uses LLM-generated SQL
7+
queries to retrieve the relevant information from a PostgreSQL database.
8+
9+
### Setup
10+
11+
The demo requires that your Google account's full name and email are configured.
12+
You can use system or env properties, see `Running the example` section below.
13+
14+
When the application starts, a registered user, the movie watcher, is allocated a random preferred movie genre .
15+
16+
The setup is defined in the [Setup.java](./src/main/java/io/quarkiverse/langchain4j/samples/chatbot/Setup.java) class.
17+
18+
The registered movie watchers are stored in a PostgreSQL database. When running the demo in dev mode (recommended), the database is automatically created and populated.
19+
20+
The genre preferred by the registered movie watcher is used by a Movie `ContentRetriever` to sort the list of movies.
21+
22+
## Running the example
23+
24+
Users must authenticate with Google before they can start working with the Movie Muse.
25+
They also must register their OpenAI API key.
26+
27+
### Google Authentication
28+
29+
In order to register an application with Google, follow steps listed in the [Quarkus Google](https://quarkus.io/guides/security-openid-connect-providers#google) section.
30+
Name your Google application as `Quarkus LangChain4j AI`, and make sure an allowed callback URL is set to `https://localhost:8443/login`.
31+
Google will generate a client id and secret, use them to set `quarkus.oidc.client-id` and `quarkus.oidc.credentials.secret` properties.
32+
33+
### OpenAI API key
34+
35+
You must provide your OpenAI API key:
36+
37+
```
38+
export QUARKUS_LANGCHAIN4J_OPENAI_API_KEY=<your-openai-api-key>
39+
```
40+
41+
Then, simply run the project in Dev mode:
42+
43+
```shell
44+
mvn quarkus:dev -Dname="Firstname Familyname" [email protected]
45+
```
46+
47+
Note, you should use double quotes to register your Google account's full name.
48+
49+
## Using the example
50+
51+
Open your browser and navigate to `https://localhost:8443`, accept the demo certificate.
52+
Now, choose a `Login with Google` option.
53+
Once logged in, click an orange robot icon in the bottom right corner to open a chat window.
54+
Ask questions such as `Give me movies with a rating higher than 8`.
55+
56+
The chatbot is available at the secure `wss:` scheme.
57+
58+
It uses a SQL database with information about movies with their
59+
basic metadata (the database is populated with data from
60+
`src/main/resources/data/movies.csv` at startup). When you ask a question, an
61+
LLM is used to generate SQL queries necessary for answering your question.
62+
Check the application's log, the SQL queries and the retrieved data will be
63+
printed there.
64+
65+
The chatbot will refer to you by your name during the conversation.
66+
67+
## Security Considerations
68+
69+
### HTTPS and WSS
70+
71+
This demo requires the use of secure HTTPS and WSS WebSockets protocols only.
72+
73+
### Chatbot is accessible to authenticated users only
74+
75+
Only users who have authenticated with Google will see a page which contains a chatbot icon.
76+
It eliminates a risk of non-authenticated users attempting to trick LLM.
77+
78+
### CORS
79+
80+
Even though browsers do not enforce Single Origin Policy (SOP) for WebSockets HTTP upgrade requests, enabling
81+
CORS origin check can add an extra protection in combination with verifying the expected authentication credentials.
82+
83+
For example, attackers can set `Origin` themselves but they will not have the HTTPS bound authentication session cookie
84+
which can be used to authenticate a WSS WebSockets upgrade request.
85+
Or if the authenticated user is tricked into visiting an unfriendly web site, then a WSS WebSockets upgrade request will fail
86+
at the Quarkus CORS check level.
87+
88+
### Custom WebSocket ticket system
89+
90+
In addition to requiring authentication over secure HTTPS, in combination with the CORS constraint for
91+
the HTTP upgrade to succeed, this demo shows a simple WebSockets ticket system to verify that the current HTTP upgrade request
92+
was made from the page allocated to authenticated users only.
93+
94+
When the user completes the OpenId Connect Google authentication, a dynamically generated HTML page will contain a WSS HTTP upgrade link
95+
with a randomly generated ticket added at the authentication time, for example: `wss://localhost/chatbot?ticket=random_ticket_value`.
96+
97+
HTTP upgrade checker will ensure that a matching ticket is found before permitting an upgrade.
98+
99+
### User identity is visible to AI services and tools
100+
101+
AI service and tools can access an ID `JsonWebToken` token which represents a successful user authentication and use it to complete its work.

samples/secure-sql-chatbot/pom.xml

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<groupId>io.quarkiverse.langchain4j</groupId>
6+
<artifactId>quarkus-langchain4j-sample-secure-sql-chatbot</artifactId>
7+
<name>Quarkus LangChain4j - Sample - Secure Chatbot &amp; RAG from a SQL database</name>
8+
<version>1.0-SNAPSHOT</version>
9+
10+
<properties>
11+
<compiler-plugin.version>3.13.0</compiler-plugin.version>
12+
<maven.compiler.parameters>true</maven.compiler.parameters>
13+
<maven.compiler.release>17</maven.compiler.release>
14+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
15+
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
16+
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
17+
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
18+
<quarkus.platform.version>3.15.1</quarkus.platform.version>
19+
<skipITs>true</skipITs>
20+
<surefire-plugin.version>3.2.5</surefire-plugin.version>
21+
<quarkus-langchain4j.version>0.21.0</quarkus-langchain4j.version>
22+
</properties>
23+
24+
<dependencyManagement>
25+
<dependencies>
26+
<dependency>
27+
<groupId>${quarkus.platform.group-id}</groupId>
28+
<artifactId>${quarkus.platform.artifact-id}</artifactId>
29+
<version>${quarkus.platform.version}</version>
30+
<type>pom</type>
31+
<scope>import</scope>
32+
</dependency>
33+
</dependencies>
34+
</dependencyManagement>
35+
36+
<dependencies>
37+
<dependency>
38+
<groupId>io.quarkus</groupId>
39+
<artifactId>quarkus-rest-jackson</artifactId>
40+
</dependency>
41+
<dependency>
42+
<groupId>io.quarkus</groupId>
43+
<artifactId>quarkus-websockets-next</artifactId>
44+
</dependency>
45+
<dependency>
46+
<groupId>io.quarkus</groupId>
47+
<artifactId>quarkus-oidc</artifactId>
48+
</dependency>
49+
<dependency>
50+
<groupId>io.quarkus</groupId>
51+
<artifactId>quarkus-jdbc-postgresql</artifactId>
52+
</dependency>
53+
<dependency>
54+
<groupId>io.quarkus</groupId>
55+
<artifactId>quarkus-hibernate-orm</artifactId>
56+
</dependency>
57+
<dependency>
58+
<groupId>io.quarkus</groupId>
59+
<artifactId>quarkus-hibernate-orm-panache</artifactId>
60+
</dependency>
61+
<dependency>
62+
<groupId>io.quarkus</groupId>
63+
<artifactId>quarkus-rest-qute</artifactId>
64+
</dependency>
65+
<dependency>
66+
<groupId>io.quarkiverse.langchain4j</groupId>
67+
<artifactId>quarkus-langchain4j-openai</artifactId>
68+
<version>${quarkus-langchain4j.version}</version>
69+
</dependency>
70+
<dependency>
71+
<groupId>com.fasterxml.jackson.dataformat</groupId>
72+
<artifactId>jackson-dataformat-csv</artifactId>
73+
</dependency>
74+
75+
76+
<!-- UI -->
77+
<dependency>
78+
<groupId>io.mvnpm</groupId>
79+
<artifactId>importmap</artifactId>
80+
<version>1.0.11</version>
81+
</dependency>
82+
<dependency>
83+
<groupId>org.mvnpm</groupId>
84+
<artifactId>lit</artifactId>
85+
<version>3.2.0</version>
86+
<scope>runtime</scope>
87+
</dependency>
88+
<dependency>
89+
<groupId>org.mvnpm</groupId>
90+
<artifactId>wc-chatbot</artifactId>
91+
<version>0.2.0</version>
92+
<scope>runtime</scope>
93+
</dependency>
94+
95+
<!-- Minimal dependencies to constrain the build -->
96+
<dependency>
97+
<groupId>io.quarkiverse.langchain4j</groupId>
98+
<artifactId>quarkus-langchain4j-openai-deployment</artifactId>
99+
<version>${quarkus-langchain4j.version}</version>
100+
<scope>test</scope>
101+
<type>pom</type>
102+
<exclusions>
103+
<exclusion>
104+
<groupId>*</groupId>
105+
<artifactId>*</artifactId>
106+
</exclusion>
107+
</exclusions>
108+
</dependency>
109+
</dependencies>
110+
<build>
111+
<plugins>
112+
<plugin>
113+
<groupId>io.quarkus</groupId>
114+
<artifactId>quarkus-maven-plugin</artifactId>
115+
<version>${quarkus.platform.version}</version>
116+
<executions>
117+
<execution>
118+
<goals>
119+
<goal>build</goal>
120+
</goals>
121+
</execution>
122+
</executions>
123+
</plugin>
124+
<plugin>
125+
<artifactId>maven-compiler-plugin</artifactId>
126+
<version>${compiler-plugin.version}</version>
127+
</plugin>
128+
<plugin>
129+
<artifactId>maven-surefire-plugin</artifactId>
130+
<version>3.5.1</version>
131+
<configuration>
132+
<systemPropertyVariables>
133+
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
134+
<maven.home>${maven.home}</maven.home>
135+
</systemPropertyVariables>
136+
</configuration>
137+
</plugin>
138+
</plugins>
139+
</build>
140+
141+
<profiles>
142+
<profile>
143+
<id>native</id>
144+
<activation>
145+
<property>
146+
<name>native</name>
147+
</property>
148+
</activation>
149+
<build>
150+
<plugins>
151+
<plugin>
152+
<artifactId>maven-failsafe-plugin</artifactId>
153+
<version>3.5.1</version>
154+
<executions>
155+
<execution>
156+
<goals>
157+
<goal>integration-test</goal>
158+
<goal>verify</goal>
159+
</goals>
160+
<configuration>
161+
<systemPropertyVariables>
162+
<native.image.path>
163+
${project.build.directory}/${project.build.finalName}-runner
164+
</native.image.path>
165+
<java.util.logging.manager>org.jboss.logmanager.LogManager
166+
</java.util.logging.manager>
167+
<maven.home>${maven.home}</maven.home>
168+
</systemPropertyVariables>
169+
</configuration>
170+
</execution>
171+
</executions>
172+
</plugin>
173+
</plugins>
174+
</build>
175+
<properties>
176+
<quarkus.package.type>native</quarkus.package.type>
177+
</properties>
178+
</profile>
179+
180+
<profile>
181+
<id>mvnpm</id>
182+
<repositories>
183+
<repository>
184+
<id>central</id>
185+
<name>central</name>
186+
<url>https://repo.maven.apache.org/maven2</url>
187+
</repository>
188+
<repository>
189+
<snapshots>
190+
<enabled>false</enabled>
191+
</snapshots>
192+
<id>mvnpm.org</id>
193+
<name>mvnpm</name>
194+
<url>https://repo.mvnpm.org/maven2</url>
195+
</repository>
196+
</repositories>
197+
</profile>
198+
</profiles>
199+
200+
</project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.quarkiverse.langchain4j.sample.chatbot;
2+
3+
import org.eclipse.microprofile.jwt.Claims;
4+
import org.eclipse.microprofile.jwt.JsonWebToken;
5+
6+
import io.quarkus.logging.Log;
7+
import io.quarkus.oidc.IdToken;
8+
import io.quarkus.security.Authenticated;
9+
import jakarta.inject.Inject;
10+
11+
import io.quarkus.websockets.next.OnOpen;
12+
import io.quarkus.websockets.next.OnTextMessage;
13+
import io.quarkus.websockets.next.WebSocket;
14+
15+
@WebSocket(path = "/chatbot")
16+
@Authenticated
17+
public class ChatBotWebSocket {
18+
19+
private final MovieMuse bot;
20+
21+
@Inject
22+
@IdToken
23+
JsonWebToken idToken;
24+
25+
public ChatBotWebSocket(MovieMuse bot) {
26+
this.bot = bot;
27+
}
28+
29+
@OnOpen
30+
public String onOpen() {
31+
return "Hello, " + idToken.getName() + ", I'm MovieMuse, how can I help you?";
32+
}
33+
34+
@OnTextMessage
35+
public String onMessage(String message) {
36+
try {
37+
return idToken.getName() + ", " + bot.chat(message);
38+
} catch (MissingMovieWatcherException ex) {
39+
Log.error(ex);
40+
return """
41+
Sorry, %s, looks like you did not register your name and email correctly.
42+
Please use '-Dname="%s"' (keep double quotes around your name) and '-Demail=%s' system properties at startup
43+
"""
44+
.formatted(idToken.getName(), idToken.getName(), idToken.getClaim(Claims.email));
45+
} catch (Throwable ex) {
46+
Log.error(ex);
47+
return "Sorry, " + idToken.getName()
48+
+ ", an unexpected error occurred, can you please ask your question again ?";
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)